Usando las herramientas propias del diálogo: --output-fd flag
Si lee la página de manual para el diálogo, hay una opción --output-fd
que le permite establecer explícitamente dónde va la salida (STDOUT 1, STDERR 2), en lugar de ir por defecto a STDERR.
A continuación puede verme ejecutando un dialog
comando de muestra , indicando explícitamente que la salida debe ir al descriptor de archivo 1, lo que me permite guardarlo en MYVAR.
MYVAR=$(dialog --inputbox "THIS OUTPUT GOES TO FD 1" 25 25 --output-fd 1)
Usar tuberías con nombre
El enfoque alternativo que tiene mucho potencial oculto es usar algo conocido como tubería con nombre .
#!/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 descripción más detallada de la respuesta de user.dz con un enfoque alternativo
La respuesta original de user.dz y la explicación de ByteCommander de eso proporcionan una buena solución y una visión general de lo que hace. Sin embargo, creo que un análisis más profundo podría ser beneficioso para explicar por qué funciona.
En primer lugar, es importante comprender dos cosas: cuál es el problema que estamos tratando de resolver y cuáles son los mecanismos subyacentes de los mecanismos de shell con los que estamos tratando. La tarea es capturar la salida de un comando mediante la sustitución de comandos. Bajo una visión general simplista que todos conocen, las sustituciones de comandos capturan el stdout
comando y lo dejan ser reutilizado por otra cosa. En este caso, la result=$(...)
parte debe guardar la salida de cualquier comando designado por ...
en una variable llamada result
.
Debajo del capó, la sustitución de comandos se implementa realmente como una tubería, donde hay un proceso secundario (el comando real que se ejecuta) y el proceso de lectura (que guarda la salida en variable). Esto es evidente con un simple rastro de llamadas al sistema. Observe que el descriptor de archivo 3 es el final de lectura de la tubería, mientras que 4 es el final de escritura. Para el proceso secundario de echo
, que escribe en él stdout
: el descriptor de archivo 1, ese descriptor de archivo es en realidad una copia del descriptor de archivo 4, que es el final de escritura de la tubería. Tenga en cuenta que stderr
no está jugando un papel aquí, simplemente porque es una tubería que stdout
solo se conecta .
$ 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 +++
Volvamos a la respuesta original por un segundo. Como ahora sabemos que dialog
escribe el cuadro TUI stdout
, responde stderr
y dentro de la sustitución de comandos stdout
se canaliza a otro lugar, ya tenemos parte de la solución: necesitamos volver a cablear los descriptores de archivos de tal manera que stderr
se canalicen al proceso del lector. Esta es la 2>&1
parte de la respuesta. Sin embargo, ¿qué hacemos con la caja TUI?
Ahí es donde entra en dup2()
juego el descriptor de archivos 3. La llamada al sistema nos permite duplicar los descriptores de archivos, haciéndolos referir efectivamente al mismo lugar, sin embargo, podemos manipularlos por separado. Los descriptores de archivos de procesos que tienen un terminal de control conectado en realidad apuntan a un dispositivo terminal específico. Esto es evidente si lo haces
$ 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
¿Dónde /dev/pts/5
está mi dispositivo pseudo-terminal actual? Por lo tanto, si de alguna manera podemos guardar este destino, aún podemos escribir el cuadro TUI en la pantalla del terminal. Eso es lo que exec 3>&1
hace. Cuando llama a un comando con redirección, command > /dev/null
por ejemplo, el shell pasa su descriptor de archivo estándar y luego lo utiliza dup2()
para escribir ese descriptor de archivo /dev/null
. El exec
comando realiza algo similar adup2()
los descriptores de archivo para toda la sesión de shell, haciendo que cualquier comando herede el descriptor de archivo ya redirigido. Lo mismo con exec 3>&1
. El descriptor de archivo 3
ahora se referirá / señalará al terminal de control, y cualquier comando que se ejecute en esa sesión de shell lo sabrá.
Entonces, cuando result=$(dialog --inputbox test 0 0 2>&1 1>&3);
ocurre, el shell crea una tubería para que el diálogo escriba, pero también 2>&1
hará que el descriptor de archivo 2 del comando se duplique en el descriptor de archivo de escritura de esa tubería (haciendo que la salida vaya al extremo de lectura de la tubería y dentro de la variable) , mientras que el descriptor de archivo 1 se duplicará en 3. Esto hará que el descriptor de archivo 1 todavía se refiera al terminal de control, y el cuadro de diálogo TUI aparecerá en la pantalla.
Ahora, en realidad hay una abreviatura para el terminal de control actual del proceso, que es /dev/tty
. Por lo tanto, la solución se puede simplificar sin el uso de descriptores de archivo, simplemente en:
result=$(dialog --inputbox test 0 0 2>&1 1>/dev/tty);
echo "$result"
Cosas clave para recordar:
- los descriptores de archivo son heredados de la shell por cada comando
- la sustitución de comandos se implementa como tubería
- los descriptores de archivos duplicados se referirán al mismo lugar que los originales, pero podemos manipular cada descriptor de archivo por separado
Ver también
mktemp
comando para crear un archivo temporal.