Ich habe mir selbst das Bash-Scripting beigebracht und bin auf ein Problem gestoßen. Ich habe ein Skript geschrieben, um mithilfe des Befehls "read" Eingaben vom Benutzer zu übernehmen und diese Eingabe zu einer Variablen zu machen, die später im Skript verwendet werden soll. Das Skript funktioniert, aber ...
Ich möchte es mit 'dialog' einrichten können. Ich habe herausgefunden, dass
'dialog --inputbox' leitet die Ausgabe an 'stderr' weiter. Um diese Eingabe als Variable zu erhalten, müssen Sie sie in eine Datei umleiten und dann abrufen. Der Code, den ich gefunden habe, um dies zu erklären, ist:
#!/bin/bash
dialog --inputbox \
"What is your username?" 0 0 2> /tmp/inputbox.tmp.$$
retval=$?
input=`cat /tmp/inputbox.tmp.$$`
rm -f /tmp/inputbox.tmp.$$
case $retval in
0)
echo "Your username is '$input'";;
1)
echo "Cancel pressed.";;
esac
Ich sehe, dass sdterr mit 2> an /tmp/inputbox.tmp.$$ gesendet wird, aber die Ausgabedatei sieht aus wie 'inputbox.tmp.21661'. Wenn ich versuche, die Datei zu durchsuchen, erhalte ich eine Fehlermeldung. Daher kann ich die Benutzereingabe immer noch nicht als Variable aus der --Eingabebox abrufen.
Beispielskript:
echo " What app would you like to remove? "
read dead_app
sudo apt-get remove --purge $dead_app
Wie Sie sehen, handelt es sich um ein einfaches Skript. Ist es überhaupt möglich, die Variable als Wort zu erhalten dialog --inputbox
?
mktemp
Befehl verwenden, um eine temporäre Datei zu erstellen.Antworten:
^ Antwort von @Sneetsher (4. Juli 2014)
Wie gewünscht werde ich versuchen zu erklären, was dieses Snippet Zeile für Zeile macht.
Beachten Sie, dass ich es vereinfachen werde, indem ich alle
;
Semikolons an den Zeilenenden weglasse , da sie nicht erforderlich sind, wenn wir einen Befehl pro Zeile schreiben.I / O-Streams:
Zunächst müssen Sie die Kommunikationsströme verstehen. Es gibt 10 Streams, die von 0 bis 9 nummeriert sind:
Stream 0 ("STDIN"):
"Standardeingabe", der Standardeingabestream zum Lesen von Daten von der Tastatur.
Stream 1 ("STDOUT"):
"Standardausgabe", der Standardausgabestream, der verwendet wird, um normalen Text im Terminal anzuzeigen .
Stream 2 ("STDERR"): "Standardfehler", der Standardausgabestream, der zum Anzeigen von Fehlern oder anderem Text für spezielle Zwecke im Terminal verwendet wird.
Streams 3-9:
Zusätzliche, frei verwendbare Streams. Sie werden standardmäßig nicht verwendet und existieren erst, wenn versucht wird, sie zu verwenden.
Beachten Sie, dass alle "Streams" intern durch Dateideskriptoren dargestellt werden
/dev/fd
(dies ist ein symbolischer Link,/proc/self/fd
der für jeden Stream einen anderen symbolischen Link enthält ... dies ist etwas kompliziert und für ihr Verhalten nicht wichtig, daher höre ich hier auf.). Die Standard-Streams haben/dev/stdin
auch/dev/stdout
und/dev/stderr
(die wiederum symbolische Links sind, etc ...).Das Drehbuch:
Die integrierte Bash-Funktion
exec
kann verwendet werden, um eine Stream-Umleitung auf die Shell anzuwenden. Dies bedeutet, dass alle folgenden Befehle davon betroffen sind. Weitere Informationen erhalten Siehelp exec
in Ihrem Terminal.In diesem speziellen Fall wird der Stream 3 auf Stream 1 (STDOUT) umgeleitet. Das bedeutet, dass alles, was wir später an Stream 3 senden, in unserem Terminal so angezeigt wird, als ob es normalerweise auf STDOUT gedruckt worden wäre.
Diese Zeile besteht aus vielen Teilen und syntaktischen Strukturen:
result=$(...)
Diese Struktur führt den Befehl in Klammern aus und weist der bash-Variablen die Ausgabe (STDOUT) zu
result
. Es ist lesbar durch$result
. Das alles ist irgendwie schon sehr lange beschriebenman bash
.dialog --inputbox TEXT HEIGHT WIDTH
Dieser Befehl zeigt eine TUI-Box mit dem angegebenen TEXT, einem Texteingabefeld und zwei Schaltflächen OK und CANCEL. Wenn OK ausgewählt wird, wird der Befehl mit dem Status 0 beendet und der eingegebene Text an STDERR ausgegeben. Wenn ABBRECHEN ausgewählt wird, wird der Befehl mit dem Code 1 beendet und es wird nichts gedruckt. Weitere Informationen finden Sie unter
man dialog
.2>&1 1>&3
Dies sind zwei Umleitungsbefehle. Sie werden von rechts nach links interpretiert:
1>&3
Leitet den Stream 1 (STDOUT) des Befehls zum benutzerdefinierten Stream 3 um.2>&1
leitet danach den Stream 2 (STDERR) des Befehls zu Stream 1 (STDOUT) um.Das bedeutet, dass alles, was der Befehl an STDOUT ausgibt, jetzt in Stream 3 angezeigt wird, während alles, was in STDERR angezeigt werden soll, jetzt an STDOUT umgeleitet wird.
Die gesamte Zeile zeigt also eine Eingabeaufforderung an (auf STDOUT, die auf Stream 3 umgeleitet wurde, die die Shell am Ende erneut auf STDOUT umleitet - siehe
exec 3>&1
Befehl) und weist die eingegebenen Daten zu (über STDERR zurückgegeben, dann auf STDOUT umgeleitet). auf die Variable Bashresult
.Dieser Code ruft den Exit-Code des zuvor ausgeführten Befehls (hier von
dialog
) über die reservierte Bash-Variable ab$?
(enthält immer den letzten Exit-Code) und speichert ihn einfach in unserer eigenen Bash-Variablenexitcode
. Es kann wieder durchgelesen$exitcode
werden. Sie können in nach weiteren Informationen suchenman bash
, aber das kann eine Weile dauern ...Die integrierte Bash-Funktion
exec
kann verwendet werden, um eine Stream-Umleitung auf die Shell anzuwenden. Dies bedeutet, dass alle folgenden Befehle davon betroffen sind. Weitere Informationen erhalten Siehelp exec
in Ihrem Terminal.In diesem speziellen Fall wird der Stream 3 zu "stream -" umgeleitet, was nur bedeutet, dass er geschlossen werden sollte. An Stream 3 gesendete Daten werden ab sofort nirgendwo mehr weitergeleitet.
Dieser einfache
echo
Befehl (weitere Informationen zuman echo
) druckt nur den Inhalt der beiden Bash-Variablenresult
undexitcode
an das STDOUT. Da wir hier keine expliziten oder impliziten Stream-Weiterleitungen mehr haben, werden sie wirklich auf STDOUT angezeigt und werden daher einfach im Terminal angezeigt. Was ein Wunder! ;-)Zusammenfassung:
Zuerst richten wir die Shell so ein, dass alles, was wir an den benutzerdefinierten Stream 3 senden, an STDOUT zurückgeleitet wird, damit es in unserem Terminal angezeigt wird.
Dann führen wir den
dialog
Befehl aus, leiten sein ursprüngliches STDOUT zu unserem benutzerdefinierten Stream 3 um, da es am Ende angezeigt werden muss, aber wir müssen den STDOUT-Stream vorübergehend für etwas anderes verwenden.Wir leiten das ursprüngliche STDERR des Befehls, bei dem die Benutzereingabe des Dialogfensters zurückgegeben wird, anschließend zu STDOUT um.
Jetzt können wir das STDOUT (das die umgeleiteten Daten von STDERR enthält) erfassen und in unserer Variablen speichern
$result
. Es enthält jetzt die gewünschte Benutzereingabe!Wir wollen auch den
dialog
Exit-Code des Befehls, der uns anzeigt, ob OK oder CANCEL angeklickt wurde. Dieser Wert wird in der reservierten Bash-Variablen dargestellt$?
und wir kopieren ihn einfach in unsere eigene Variable$exitcode
.Danach schließen wir Stream 3 wieder, da wir ihn nicht mehr benötigen, um weitere Umleitungen zu stoppen.
Schließlich geben wir normalerweise den Inhalt beider Variablen
$result
(die Benutzereingabe des Dialogfensters) und$exitcode
(0 für OK, 1 für ABBRECHEN) an das Terminal aus.quelle
exec
ist unnötig kompliziert. Warum nicht einfach die--stdout
Optiondialog
oder Umleitung der Ausgabe durch uns2>&1 >/dev/tty
?Dialogeigene Tools verwenden: --output-fd flag
Wenn Sie die Manpage für den Dialog lesen, gibt es eine Option
--output-fd
, mit der Sie explizit festlegen können, wohin die Ausgabe geht (STDOUT 1, STDERR 2), anstatt standardmäßig zu STDERR.Unten sehen Sie, wie ich den Beispielbefehl
dialog
ausführe, wobei explizit angegeben wird, dass die Ausgabe in Dateideskriptor 1 erfolgen muss, damit ich sie in MYVAR speichern kann.MYVAR=$(dialog --inputbox "THIS OUTPUT GOES TO FD 1" 25 25 --output-fd 1)
Named Pipes verwenden
Ein alternativer Ansatz, der viel verborgenes Potenzial birgt, ist die Verwendung einer sogenannten Named Pipe .
Eine detailliertere Übersicht über die Antwort von user.dz mit alternativen Ansätzen
Die ursprüngliche Antwort von user.dz und die Erklärung von ByteCommander bieten eine gute Lösung und einen Überblick über die Funktionsweise. Ich glaube jedoch, dass eine tiefere Analyse hilfreich sein könnte, um zu erklären, warum es funktioniert.
Zuallererst ist es wichtig, zwei Dinge zu verstehen: Was ist das Problem, das wir lösen wollen, und wie funktionieren die Shell-Mechanismen, mit denen wir es zu tun haben? Die Aufgabe besteht darin, die Ausgabe eines Befehls durch Befehlsersetzung zu erfassen. Unter einem einfachen Überblick, den jeder kennt, erfassen Befehlsersetzungen den
stdout
eines Befehls und lassen ihn von etwas anderem wiederverwenden. In diesem Fall sollte dasresult=$(...)
Teil die Ausgabe des Befehls, der von bezeichnet wird,...
in einer aufgerufenen Variablen speichernresult
.Unter der Haube wird die Befehlsersetzung tatsächlich als Pipe implementiert, wobei ein untergeordneter Prozess (der aktuelle ausgeführte Befehl) und ein Leseprozess (der die Ausgabe in einer Variablen speichert) vorhanden sind. Dies wird anhand einer einfachen Verfolgung von Systemaufrufen deutlich. Beachten Sie, dass der Dateideskriptor 3 das Leseende der Pipe ist, während 4 das Schreibende ist. Für den untergeordneten Prozess von
echo
, der in seinenstdout
- Dateideskriptor 1 schreibt , ist dieser Dateideskriptor tatsächlich eine Kopie des Dateideskriptors 4, der das Schreibende der Pipe ist. Beachten Sie, dass diesstderr
hier keine Rolle spielt, da es sichstdout
lediglich um eine Rohrverbindung handelt .Kehren wir für eine Sekunde zur ursprünglichen Antwort zurück. Da wir jetzt wissen, dass
dialog
das TUI-Feld an eine andere Stelle geschriebenstdout
, beantwortetstderr
und innerhalb der Befehlsersetzung an einestdout
andere Stelle weitergeleitet wird, haben wir bereits einen Teil der Lösung - wir müssen die Dateideskriptoren so umverdrahten, dass sie an den Leseprozess weitergeleitetstderr
werden. Dies ist der2>&1
Teil der Antwort. Was machen wir aber mit der TUI Box?Hier kommt der Dateideskriptor 3 ins
dup2()
Spiel . Der Syscall ermöglicht es uns, Dateideskriptoren zu duplizieren, sodass sie effektiv auf denselben Ort verweisen, aber wir können sie separat bearbeiten. Dateideskriptoren von Prozessen, an die ein steuerndes Terminal angeschlossen ist, verweisen tatsächlich auf ein bestimmtes Terminalgerät. Dies ist offensichtlich, wenn Sie dies tunWo
/dev/pts/5
ist mein aktuelles Pseudo-Endgerät? Wenn wir dieses Ziel also irgendwie speichern können, können wir die TUI-Box immer noch auf den Terminalbildschirm schreiben. Das ist wasexec 3>&1
tut. Wenn Sie beispielsweise einen Befehl mit Umleitung aufrufencommand > /dev/null
, übergibt die Shell den stdout-Dateideskriptor unddup2()
schreibt diesen Dateideskriptor dann an/dev/null
. Derexec
Befehl führt für die gesamte Shell-Sitzung etwas Ähnliches wiedup2()
Dateideskriptoren aus, sodass alle Befehle bereits umgeleitete Dateideskriptoren übernehmen. Gleiche mitexec 3>&1
. Der Dateideskriptor3
verweist nun auf das steuernde Terminal, und jeder Befehl, der in dieser Shell-Sitzung ausgeführt wird, weiß davon.Wenn dies
result=$(dialog --inputbox test 0 0 2>&1 1>&3);
auftritt, erstellt die Shell eine Pipe für den Dialog zum Schreiben, lässt jedoch2>&1
zuerst den Dateideskriptor 2 des Befehls auf den Dateideskriptor dieser Pipe duplizieren (wodurch die Ausgabe zum Lesen des Endes der Pipe und in die Variable erfolgt). Der Dateideskriptor 1 wird auf 3 dupliziert. Dadurch verweist der Dateideskriptor 1 weiterhin auf das steuernde Terminal, und der TUI-Dialog wird auf dem Bildschirm angezeigt.Nun, es gibt tatsächlich eine Abkürzung für das derzeitige steuernde Terminal des Prozesses
/dev/tty
. Somit kann die Lösung ohne Verwendung von Dateideskriptoren vereinfacht werden:Wichtige Dinge, an die Sie sich erinnern sollten:
Siehe auch
quelle
--stdout
Option gefährlich sein kann und auf manchen Systemen leicht ausfällt, und ich denke--output-fd 1
, dasselbe gilt:--stdout: Direct output to the standard output. This option is provided for compatibility with Xdialog, however using it in portable scripts is not recommended, since curses normally writes its screen updates to the standard output. If you use this option, dialog attempts to reopen the terminal so it can write to the display. Depending on the platform and your environment, that may fail.
- Die Named-Pipe-Idee ist jedoch cool!--output-fd
, was die Option ist, die ich hier verwendet habe, nicht--stdout
. Zweitens wird der Dialog zuerst auf stdout gezeichnet, die zurückgegebene Ausgabe ist second. Wir machen diese beiden Dinge nicht gleichzeitig. Es ist--output-fd
jedoch nicht ausdrücklich erforderlich, dass fd 1 (STDOUT) verwendet wird. Es kann leicht zu einem anderen Dateideskriptor umgeleitet werden: DI kann es nicht erklären !!! Wenn Sie verstehen können, was sie in der Referenz sagen: Erweitertes Bash-Scripting-Handbuch: Kapitel 20. E / A-Umleitung , schreiben Sie eine neue Antwort und ich gebe Ihnen 50repFür eine Erklärung siehe die Antwort von ByteCommander . :) Dies ist ein Teil der Geschichte.
Quelle: Dialog in Bash erfasst Variablen nicht korrekt.
Referenz: Erweitertes Bash-Scripting-Handbuch: Kapitel 20. E / A-Umleitung
quelle
Das funktioniert bei mir:
Die Handbuchseite von
dialog
erzählt über --stdout:Kann jemand sagen, in welcher Plattform oder Umgebung es nicht funktioniert? Funktioniert die Umleitung der
dialog
Ausgabe auf2>&1 >/dev/tty
besser?quelle
Für den Fall, dass jemand anderes von Google hier gelandet ist und diese Frage speziell nach bash fragt, gibt es hier eine andere Alternative:
Sie können Zenity verwenden . Zenity ist eine grafische Dienstprogramm , das kann innerhalb Bash - Skripte verwendet. Aber dies würde natürlich einen X-Server erfordern, wie user877329 zu Recht betont.
Dann in Ihrem Skript:
Nützlicher Link .
quelle
dialog
. Es ist so, als würde ich Sie fragen: "Wie schreibe ich dies und das in Python?", Aber Sie geben mir Bash - ich bin sehr froh, dass dies auf andere Weise gemacht werden kann, aber dasDie Antwort von Sneetsher ist etwas eleganter, aber ich kann erklären, was falsch ist: Der Wert von
$$
ist in den Backticks unterschiedlich (weil es eine neue Shell startet und$$
die PID der aktuellen Shell ist). Sie möchten den Dateinamen in eine Variable einfügen und sich stattdessen durchgehend auf diese Variable beziehen.In diesem Fall ist das Vermeiden der temporären Datei eine bessere Lösung. In vielen Situationen können Sie eine temporäre Datei jedoch nicht vermeiden.
quelle