Utilizzando gli strumenti propri della finestra di dialogo: --output-fd flag
Se leggi la pagina man per la finestra di dialogo, c'è un'opzione --output-fd
che ti permette di impostare esplicitamente dove va l'output (STDOUT 1, STDERR 2), invece di andare su STDERR per impostazione predefinita.
Qui sotto puoi vedermi eseguire il dialog
comando di esempio , affermando esplicitamente che l'output deve andare al descrittore di file 1, che mi consente di salvarlo in MYVAR.
MYVAR=$(dialog --inputbox "THIS OUTPUT GOES TO FD 1" 25 25 --output-fd 1)
Utilizzando pipe denominate
Un approccio alternativo che ha molte potenzialità nascoste è quello di usare qualcosa noto come 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
Una panoramica più approfondita della risposta di user.dz con approccio alternativo
La risposta originale di user.dz e la spiegazione di ByteCommander forniscono entrambe una buona soluzione e una panoramica di ciò che fa. Tuttavia, credo che un'analisi più approfondita potrebbe essere utile per spiegare perché funziona.
Prima di tutto, è importante capire due cose: qual è il problema che stiamo cercando di risolvere e quali sono i meccanismi alla base dei meccanismi di shell con cui abbiamo a che fare. Il compito è acquisire l'output di un comando tramite la sostituzione del comando. Sotto una visione semplicistica che tutti conoscono, le sostituzioni di comandi catturano il stdout
comando e lo lasciano riutilizzare da qualcos'altro. In questo caso, la result=$(...)
parte dovrebbe salvare l'output di qualunque comando sia designato ...
in una variabile chiamata result
.
Sotto il cofano, la sostituzione dei comandi è effettivamente implementata come pipe, dove c'è un processo figlio (il comando effettivo che viene eseguito) e un processo di lettura (che salva l'output in variabile). Ciò è evidente con una semplice traccia di chiamate di sistema. Si noti che il descrittore di file 3 è la fine della lettura della pipe, mentre 4 è la fine della scrittura. Per il processo figlio di echo
, che scrive nel suo stdout
- il descrittore di file 1, quel descrittore di file è in realtà copia del descrittore di file 4, che è l'estremità di scrittura della pipe. Si noti che qui stderr
non sta giocando un ruolo, semplicemente perché è stdout
solo una pipe .
$ 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 +++
Torniamo alla risposta originale per un secondo. Da ora sappiamo che dialog
scrive la casella TUI su stdout
, rispondere stderr
e all'interno della sostituzione dei comandi stdout
viene convogliata da qualche altra parte, abbiamo già parte della soluzione - dobbiamo ricollegare i descrittori di file in modo tale che stderr
vengano convogliati al processo di lettura. Questa è la 2>&1
parte della risposta. Tuttavia, cosa facciamo con la TUI box?
È qui che entra in gioco il descrittore di file 3. La dup2()
syscall ci consente di duplicare i descrittori di file, facendoli effettivamente fare riferimento allo stesso posto, ma possiamo manipolarli separatamente. I descrittori di file di processi a cui è collegato un terminale di controllo in realtà puntano a un dispositivo terminale specifico. Questo è evidente se lo fai
$ 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
dov'è il /dev/pts/5
mio attuale dispositivo pseudo-terminale. Pertanto, se possiamo in qualche modo salvare questa destinazione, possiamo ancora scrivere la casella TUI sullo schermo del terminale. Questo è quello che exec 3>&1
fa. Quando si chiama un comando con il reindirizzamento, command > /dev/null
ad esempio, la shell passa il suo descrittore di file stdout e quindi usa dup2()
per scrivere quel descrittore di file /dev/null
. Il exec
comando esegue qualcosa di simile aidup2()
descrittori di file per l'intera sessione della shell, facendo in modo che qualsiasi comando erediti il descrittore di file già reindirizzato. Lo stesso con exec 3>&1
. Il descrittore di file 3
ora farà riferimento a / punta al terminale di controllo e qualsiasi comando eseguito in quella sessione della shell lo saprà.
Quindi, quando si result=$(dialog --inputbox test 0 0 2>&1 1>&3);
verifica, la shell crea una pipe per la finestra di dialogo da scrivere, ma 2>&1
prima farà anche duplicare il descrittore di file del comando 2 sul descrittore di file di scrittura di quella pipe (facendo in modo che l'output vada a leggere la fine della pipe e nella variabile) , mentre il descrittore di file 1 verrà duplicato su 3. In questo modo il descrittore di file 1 farà ancora riferimento al terminale di controllo e la finestra di dialogo TUI apparirà sullo schermo.
Ora, in realtà c'è una scorciatoia per l'attuale terminale di controllo del processo, che è /dev/tty
. Pertanto, la soluzione può essere semplificata senza l'uso di descrittori di file, semplicemente in:
result=$(dialog --inputbox test 0 0 2>&1 1>/dev/tty);
echo "$result"
Cose chiave da ricordare:
- i descrittori di file sono ereditati dalla shell da ciascun comando
- la sostituzione del comando è implementata come pipe
- descrittori di file duplicati faranno riferimento allo stesso posto di quello originale, ma possiamo manipolare ciascun descrittore di file separatamente
Guarda anche
mktemp
comando per creare un file temporaneo.