Me he estado enseñando a mí mismo bash scripting y me he encontrado con un problema. He escrito una secuencia de comandos para recibir información del usuario, utilizando el comando 'leer', y convertir esa entrada en una variable para usar más adelante en la secuencia de comandos. El guión funciona, pero ...
Me gustaría poder configurarlo usando 'dialog'. Descubrí que
'dialog --inputbox' dirigirá la salida a 'stderr' y para obtener esa entrada como una variable, debe dirigirla a un archivo y luego recuperarla. El código que encontré para explicar esto es:
#!/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
Veo que está enviando el sdterr a /tmp/inputbox.tmp.$$ con 2>, pero el archivo de salida se ve como 'inputbox.tmp.21661'. Cuando intento capturar el archivo, me da un error. Por lo tanto, todavía no puedo obtener la entrada del usuario desde --inputbox como una variable.
Script de ejemplo:
echo " What app would you like to remove? "
read dead_app
sudo apt-get remove --purge $dead_app
Como puede ver, es un script básico. ¿Es posible obtener la variable como una palabra dialog --inputbox
?
mktemp
comando para crear un archivo temporal.Respuestas:
^ respuesta de @Sneetsher (4 de julio de 2014)
Según lo solicitado, intentaré explicar lo que está haciendo este fragmento línea por línea.
Tenga en cuenta que lo simplificaré omitiendo todos los
;
puntos y comas en los extremos de la línea, porque no son necesarios si escribimos un comando por línea.E / S - Streams:
Primero, debe comprender los flujos de comunicación. Hay 10 transmisiones, numeradas del 0 al 9:
Secuencia 0 ("STDIN"):
"Entrada estándar", la secuencia de entrada predeterminada para leer datos desde el teclado.
Secuencia 1 ("STDOUT"):
"Salida estándar", la secuencia de salida predeterminada utilizada para mostrar texto normal en el terminal.
Secuencia 2 ("STDERR"): "Error estándar", la secuencia de salida predeterminada utilizada para mostrar errores u otro texto para fines especiales en el terminal.
Secuencias 3-9: Secuencias
adicionales de uso libre. No se usan por defecto y no existen hasta que algo intenta usarlos.
Tenga en cuenta que todas las "transmisiones" están representadas internamente por descriptores de archivo
/dev/fd
(que es un enlace simbólico/proc/self/fd
que contiene otro enlace simbólico para cada transmisión ... es un poco complicado y no es importante para su comportamiento, así que me detengo aquí). Las secuencias estándar también tienen/dev/stdin
,/dev/stdout
y/dev/stderr
(que son enlaces simbólicos de nuevo, etc.).La secuencia de comandos:
El Bash incorporado
exec
se puede usar para aplicar una redirección de flujo al shell, lo que significa que afecta a todos los siguientes comandos. Para más información, correhelp exec
en tu terminal.En este caso especial, la secuencia 3 se redirige a la secuencia 1 (STDOUT), lo que significa que todo lo que enviemos a la secuencia 3 más tarde aparecerá en nuestro terminal como si normalmente se imprimiera en STDOUT.
Esta línea consta de muchas partes y estructuras sintácticas:
result=$(...)
Esta estructura ejecuta el comando entre paréntesis y asigna la salida (STDOUT) a la variable bash
result
. Es legible a través de$result
. Todo esto se describe de alguna manera en muuuucho muuuuchoman bash
.dialog --inputbox TEXT HEIGHT WIDTH
Este comando muestra un cuadro TUI con el TEXTO dado, un campo de entrada de texto y dos botones Aceptar y CANCELAR. Si se selecciona OK, el comando sale con el estado 0 e imprime el texto ingresado en STDERR, si CANCEL se selecciona, saldrá con el código 1 y no imprimirá nada. Para más información, lea
man dialog
.2>&1 1>&3
Estos son dos comandos de redireccionamiento. Serán interpretados de derecha a izquierda:
1>&3
redirige la secuencia del comando 1 (STDOUT) a la secuencia personalizada 3.2>&1
luego redirige la secuencia 2 del comando (STDERR) a la secuencia 1 (STDOUT).Eso significa que todo lo que el comando imprime en STDOUT ahora aparece en la secuencia 3, mientras que todo lo que estaba destinado a aparecer en STDERR ahora se redirige a STDOUT.
Entonces, la línea completa muestra un mensaje de texto (en STDOUT, que se redirigió a la secuencia 3, que el shell vuelve a redirigir nuevamente a STDOUT al final - vea el
exec 3>&1
comando) y asigna los datos ingresados (devueltos a través de STDERR, luego redirigidos a STDOUT) a la variable Bashresult
.Este código recupera el código de salida del comando ejecutado previamente (aquí desde
dialog
) a través de la variable Bash reservada$?
(siempre contiene el último código de salida) y simplemente lo almacena en nuestra propia variable Bashexitcode
. Se puede leer de$exitcode
nuevo. Puede buscar más información sobre esto enman bash
, pero eso puede llevar un tiempo ...El Bash incorporado
exec
se puede usar para aplicar una redirección de flujo al shell, lo que significa que afecta a todos los siguientes comandos. Para más información, correhelp exec
en tu terminal.En este caso especial, la secuencia 3 se redirige a "secuencia -", lo que significa que debe cerrarse. Los datos enviados a la transmisión 3 ya no se redirigirán a ninguna parte a partir de ahora.
Este
echo
comando simple (más informaciónman echo
) simplemente imprime el contenido de las dos variables Bashresult
yexitcode
STDOUT. Como ya no tenemos redirecciones de flujo explícitas o implícitas aquí, realmente aparecerán en STDOUT y, por lo tanto, simplemente se mostrarán en el terminal. ¡Que milagro! ;-)Resumen:
Primero, configuramos el shell para redirigir todo lo que enviamos a la secuencia personalizada 3 de vuelta a STDOUT, para que aparezca en nuestro terminal.
Luego ejecutamos el
dialog
comando, redirigimos su STDOUT original a nuestra secuencia personalizada 3, porque debe mostrarse al final, pero necesitamos usar temporalmente la secuencia STDOUT para otra cosa.Redirigimos el STDERR original del comando, donde se devuelve la entrada del usuario de la ventana de diálogo, a STDOUT después.
Ahora podemos capturar el STDOUT (que contiene los datos redirigidos de STDERR) y almacenarlo en nuestra variable
$result
. Contiene la entrada de usuario deseada ahora!También queremos el
dialog
código de salida del comando, que nos muestra si se hizo clic en Aceptar o CANCELAR. Este valor se presenta en la variable Bash reservada$?
y simplemente lo copiamos a nuestra propia variable$exitcode
.Después de eso, cerramos la transmisión 3 nuevamente, ya que ya no la necesitamos, para detener futuras redirecciones de la misma.
Finalmente, normalmente enviamos el contenido de ambas variables
$result
(la entrada del usuario de la ventana de diálogo) y$exitcode
(0 para OK, 1 para CANCEL) al terminal.fuente
exec
es innecesariamente complicado. ¿Por qué no solo nosotros--stdout
optamos pordialog
o redirigimos su salida2>&1 >/dev/tty
?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 .
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, laresult=$(...)
parte debe guardar la salida de cualquier comando designado por...
en una variable llamadaresult
.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 élstdout
: 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 questderr
no está jugando un papel aquí, simplemente porque es una tubería questdout
solo se conecta .Volvamos a la respuesta original por un segundo. Como ahora sabemos que
dialog
escribe el cuadro TUIstdout
, respondestderr
y dentro de la sustitución de comandosstdout
se canaliza a otro lugar, ya tenemos parte de la solución: necesitamos volver a cablear los descriptores de archivos de tal manera questderr
se canalicen al proceso del lector. Esta es la2>&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¿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 queexec 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 utilizadup2()
para escribir ese descriptor de archivo/dev/null
. Elexec
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 conexec 3>&1
. El descriptor de archivo3
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én2>&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:Cosas clave para recordar:
Ver también
fuente
--stdout
opción puede ser peligrosa y está fallando fácilmente en algunos sistemas, y creo que--output-fd 1
está haciendo lo mismo:--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.
- Sin embargo, ¡la idea de tubería nombrada es genial!--output-fd
, que es la opción que usé aquí, no--stdout
. En segundo lugar, el cuadro de diálogo se dibuja en stdout primero, el resultado devuelto es el segundo. No hacemos estas dos cosas al mismo tiempo. Sin embargo,--output-fd
no requiere específicamente que uno use fd 1 (STDOUT). Se puede redirigir fácilmente a otro descriptor de archivo: DI no puedo explicarlo !!! Si puede entender lo que están diciendo en la referencia: Guía avanzada de secuencias de comandos Bash: Capítulo 20. Redirección de E / S , escriba una nueva respuesta y le daré 50 rep.Se dio recompensa, para una explicación, ver la respuesta de ByteCommander . :) Esto es parte de la historia.
Fuente: Diálogo en bash no está tomando variables correctamente
Referencia: Guía avanzada de secuencias de comandos Bash: Capítulo 20. Redirección de E / S
fuente
Esto funciona para mi:
La página de manual de
dialog
informa sobre --stdout:¿Alguien puede decir en qué plataforma o entorno no funciona? ¿Redirigir la
dialog
salida para2>&1 >/dev/tty
que funcione mejor entonces?fuente
En caso de que alguien más haya aterrizado aquí desde Google, y aunque esta pregunta pide específicamente bash, aquí hay otra alternativa:
Puedes usar zenity . Zenity es una utilidad gráfica que se puede usar dentro de los scripts de bash. Pero, por supuesto, esto requeriría un servidor X como el usuario 877329 señaló con razón.
Luego en tu guión:
Enlace útil .
fuente
dialog
. Es como si venga y te pregunte "¿Cómo escribo esto y aquello en Python?", Pero me das un golpe, estoy muy feliz de que esto se pueda hacer de otra manera, pero eso no es lo que te pidoLa respuesta proporcionada por Sneetsher es algo más elegante, pero puedo explicar lo que está mal: el valor de
$$
es diferente dentro de los backticks (porque inicia un nuevo shell y$$
es el PID del shell actual). Deberá poner el nombre del archivo en una variable, luego, consulte esa variable en su lugar.En este caso, evitar el archivo temporal sería una mejor solución, pero habrá muchas situaciones en las que no podrá evitar un archivo temporal.
fuente