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 dialogausfü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 .
#!/bin/bash
mkfifo /tmp/namedPipe1 # this creates named pipe, aka fifo
# to make sure the shell doesn't hang, we run redirection
# in background, because fifo waits for output to come out
dialog --inputbox "This is an input box with named pipe" 40 40 2> /tmp/namedPipe1 &
# release contents of pipe
OUTPUT="$( cat /tmp/namedPipe1 )"
echo "This is the output " $OUTPUT
# clean up
rm /tmp/namedPipe1

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 stdouteines Befehls und lassen ihn von etwas anderem wiederverwenden. In diesem Fall sollte das result=$(...)Teil die Ausgabe des Befehls, der von bezeichnet wird, ...in einer aufgerufenen Variablen speichern result.
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 seinen stdout- Dateideskriptor 1 schreibt , ist dieser Dateideskriptor tatsächlich eine Kopie des Dateideskriptors 4, der das Schreibende der Pipe ist. Beachten Sie, dass dies stderrhier keine Rolle spielt, da es sich stdoutlediglich um eine Rohrverbindung handelt .
$ strace -f -e pipe,dup2,write,read bash -c 'v=$(echo "X")'
...
pipe([3, 4]) = 0
strace: Process 6200 attached
[pid 6199] read(3, <unfinished ...>
[pid 6200] dup2(4, 1) = 1
[pid 6200] write(1, "X\n", 2 <unfinished ...>
[pid 6199] <... read resumed> "X\n", 128) = 2
[pid 6200] <... write resumed> ) = 2
[pid 6199] read(3, "", 128) = 0
[pid 6200] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=6200, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++
Kehren wir für eine Sekunde zur ursprünglichen Antwort zurück. Da wir jetzt wissen, dass dialogdas TUI-Feld an eine andere Stelle geschrieben stdout, beantwortet stderrund innerhalb der Befehlsersetzung an eine stdoutandere Stelle weitergeleitet wird, haben wir bereits einen Teil der Lösung - wir müssen die Dateideskriptoren so umverdrahten, dass sie an den Leseprozess weitergeleitet stderrwerden. Dies ist der 2>&1Teil 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 tun
$ ls -l /proc/self/fd
total 0
lrwx------ 1 user1 user1 64 Aug 20 10:30 0 -> /dev/pts/5
lrwx------ 1 user1 user1 64 Aug 20 10:30 1 -> /dev/pts/5
lrwx------ 1 user1 user1 64 Aug 20 10:30 2 -> /dev/pts/5
lr-x------ 1 user1 user1 64 Aug 20 10:30 3 -> /proc/6424/fd
Wo /dev/pts/5ist 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 was exec 3>&1tut. Wenn Sie beispielsweise einen Befehl mit Umleitung aufrufen command > /dev/null, übergibt die Shell den stdout-Dateideskriptor und dup2()schreibt diesen Dateideskriptor dann an /dev/null. Der execBefehl führt für die gesamte Shell-Sitzung etwas Ähnliches wiedup2() Dateideskriptoren aus, sodass alle Befehle bereits umgeleitete Dateideskriptoren übernehmen. Gleiche mit exec 3>&1. Der Dateideskriptor 3verweist 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 jedoch 2>&1zuerst 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:
result=$(dialog --inputbox test 0 0 2>&1 1>/dev/tty);
echo "$result"
Wichtige Dinge, an die Sie sich erinnern sollten:
- Dateideskriptoren werden von jedem Befehl von der Shell geerbt
- Die Befehlssubstitution wird als Pipe implementiert
- doppelte Dateideskriptoren beziehen sich auf dieselbe Stelle wie die ursprüngliche, aber wir können jeden Dateideskriptor separat bearbeiten
Siehe auch
mktempBefehl verwenden, um eine temporäre Datei zu erstellen.