
Tengo problemas con un caso que veo como un error en el funcionamiento del shell o bash, en el que se ejecuta un comando no interactivo proporcionado desde un heredoc (tal vez también desde un archivo, no lo he probado) y un read
(o similar) comando en el heredoc, hace que el código después de un read
comando seaignorado, y la ejecución hasta el finalsin errores, ejecutando los comandosdespuésel comando que ejecuta el heredoc.
Más específicamente, estoy ejecutando código en un contenedor acoplable que actúa como una caja de herramientas para varias utilidades de Linux, y este problema hace que el código no sea realmente seguro en el sentido de que no puedo confiar en que suceda como espero.
A continuación se puede ver una muestra del problema:
#!/bin/bash
set -eou pipefail
echo "before outside" >&2
docker run --rm -i ubuntu:20.04 /bin/bash <<-SHELL
set -eou pipefail
echo "before inside" >&2
read 'var'
echo "after inside" >&2
SHELL
echo "after outside" >&2
Esperaba que el código anterior ejecutara todos los ecos y finalizara correctamente, o diera un error en el read
comando y finalizara con un error.
Desafortunadamente, el resultado del código anterior es:
before outside
before inside
after outside
Básicamente ignoró lo que había después del read
comando., tal vez porque tiene una read
instrucción en un comando que no asigna un pseudo-tty ( docker run -it
asigna un pseudo-tty, pero no puede ejecutar el código anterior, debido al documento heredoc que está canalizado al comando, en lugar de eso, proporciona el error: the input device is not a TTY
).
Si elimino el 2 set -eou pipefail
tengo el mismo problema, incluso usando read 'var' ||:
, así que no creo que esté relacionado con (uno de lostrampasde) set -e
.
Un ejemplo de un resultado esperado es el siguiente:
#!/bin/bash
set -eou pipefail
echo "before outside" >&2
docker run --rm -i ubuntu:20.04 /bin/bash <<-SHELL
set -eou pipefail
echo "before inside" >&2
unknow_command error ||:
echo "after inside" >&2
SHELL
echo "after outside" >&2
Lo cual finaliza con éxito e imprime:
before outside
before inside
/bin/bash: line 3: unknow_command: command not found
after inside
after outside
O:
#!/bin/bash
set -eou pipefail
echo "before outside" >&2
docker run --rm -i ubuntu:20.04 /bin/bash <<-SHELL
set -eou pipefail
echo "before inside" >&2
unknow_command error
echo "after inside" >&2
SHELL
echo "after outside" >&2
Lo cual termina con un error e imprime:
before outside
before inside
/bin/bash: line 3: unknow_command: command not found
Para mí, estaría bien si el read
comando simplemente se omitiera (con un valor vacío como entrada, por ejemplo) y se ejecutaran todos los comandos posteriores, incluso los del heredoc, o que diera un error y detuviera la ejecución inmediatamente (si No ignoro el error en el comando de la ventana acoplable).
Lo anterior fue solo un ejemplo, en un caso real puede ser mucho peor porque el read
comando (o similar) no puede llamarse directamente, sino dentro de un comando, y solo bajo algunas condiciones. Por ejemplo:
#!/bin/bash
set -eou pipefail
docker run --rm -i my_mysql /bin/bash <<-SHELL
set -eou pipefail
some_important_command_1
mysql -u "$user" -p "$pass" -e "some sql command"
some_important_command_2
SHELL
some_important_command_after_2
El código anterior puede parecer correcto, pero si la contraseña está vacía, intentará leer desde la entrada estándar, causando el problema en el primer ejemplo, omitiendo some_important_command_2
, pero ejecutándose some_important_command_after_2
solo debería ejecutarse después de some_important_command_2
.
El ejemplo anterior de MySQL también fue solo un ejemplo. En este caso puedo verificar si la contraseña está vacía y manejarla.El verdadero problema es que no puedo determinar si este problema puede ocurrir dentro de un código y no veo una forma segura de evitarlo., además de detener el uso del contenedor de la caja de herramientas de Docker e instalar todas las utilidades y otras cosas dentro de todos los hosts, y mantenerlos actualizados (en lugar de simplemente mantener actualizada la imagen del contenedor). Tampoco funcionará para comandos que se ejecutan específicamente en servicios dentro de contenedores (como el ejemplo de MySQL anterior).
¿Alguien tiene una solución para el problema anterior?Una solución queno es especificoa los ejemplos que di, pero un enfoque general que puede resolvereste tipo de error(ya sea completando con éxito, ejecutando todos los comandos o dando un error y deteniendo todos los comandos posteriores).
Actualizar:
Agregar declare -p var
después de echo "after inside"
las salidas:
before outside
before inside
declare -- var="echo \"after inside\" >&2"
after outside
Supongo que el comando de lectura termina leyendo del heredoc como señaló @muru en los comentarios. También puedo verlo si agrego echo "var=\$var"
después echo "after inside"
, que imprime var=echo "after inside" >&2
:. Así que ahora tengo que encontrar una manera de omitir esas lecturas del propio documento heredoc.