¿Hay algo similar a pipefail para múltiples comandos, como una declaración 'try' pero dentro de bash? Me gustaría hacer algo como esto:
echo "trying stuff"
try {
command1
command2
command3
}
Y en cualquier momento, si algún comando falla, abandone y repita el error de ese comando. No quiero tener que hacer algo como:
command1
if [ $? -ne 0 ]; then
echo "command1 borked it"
fi
command2
if [ $? -ne 0 ]; then
echo "command2 borked it"
fi
Y así sucesivamente ... o algo así como:
pipefail -o
command1 "arg1" "arg2" | command2 "arg1" "arg2" | command3
Porque creo que los argumentos de cada comando (corrígeme si me equivoco) interferirán entre sí. Estos dos métodos me parecen horriblemente largos y desagradables, así que estoy aquí pidiendo un método más eficiente.
set -euo pipefail
.set -e
es una idea horrible . Vea los ejercicios en BashFAQ # 105 discutiendo solo algunos de los casos extremos inesperados que presenta, y / o la comparación que muestra incompatibilidades entre diferentes implementaciones de shells (y versiones de shell) en in-ulm.de/~mascheck/various/set -e .Respuestas:
Puede escribir una función que inicie y pruebe el comando por usted. Asume
command1
ycommand2
son variables de entorno que se han establecido en un comando.fuente
$*
, fallará si algún argumento tiene espacios; utilizar"$@"
en su lugar. Del mismo modo, poner$1
dentro de las comillas en elecho
comando.test
ya que es un comando incorporado.ls
. Si invocals foo
y recibe un mensaje de error del formulariols: foo: No such file or directory\n
, comprende el problema. Si, en cambio,ls: foo: No such file or directory\nerror with ls\n
te distraes con información superflua. En este caso, es bastante fácil argumentar que la superfluidad es trivial, pero crece rápidamente. Los mensajes de error concisos son importantes. Pero lo más importante, este tipo de envoltorio alienta a los escritores a omitir por completo los buenos mensajes de error.¿Qué quieres decir con "abandonar y repetir el error"? Si quiere decir que desea que la secuencia de comandos finalice tan pronto como falle un comando, simplemente haga
al comienzo del guión (pero tenga en cuenta la advertencia a continuación). No se moleste en hacer eco del mensaje de error: deje que el comando que falla se encargue de eso. En otras palabras, si lo haces:
y command2 falla, al imprimir un mensaje de error en stderr, parece que ha logrado lo que desea. (¡A menos que malinterprete lo que quieres!)
Como corolario, cualquier comando que escriba debe comportarse bien: debe informar errores a stderr en lugar de stdout (el código de muestra en la pregunta imprime errores a stdout) y debe salir con un estado distinto de cero cuando falla.
Sin embargo, ya no considero que esto sea una buena práctica.
set -e
ha cambiado su semántica con diferentes versiones de bash, y aunque funciona bien para un script simple, hay tantos casos extremos que es esencialmente inutilizable. (Considere cosas como:set -e; foo() { false; echo should not print; } ; foo && echo ok
la semántica aquí es algo razonable, pero si refactoriza el código en una función que se basa en la configuración de la opción para terminar antes, puede ser mordido fácilmente). En mi opinión, es mejor escribir:o
fuente
trap some_func 0
se ejecutarásome_func
a la salida)|| exit
explícitamente después de cada comando.Tengo un conjunto de funciones de secuencias de comandos que uso ampliamente en mi sistema Red Hat. Utilizan las funciones del sistema
/etc/init.d/functions
para imprimir indicadores de estado verde[ OK ]
y rojo[FAILED]
.Opcionalmente, puede establecer la
$LOG_STEPS
variable en un nombre de archivo de registro si desea registrar qué comandos fallan.Uso
Salida
Código
fuente
Para lo que vale, una forma más corta de escribir código para verificar el éxito de cada comando es:
Todavía es tedioso, pero al menos es legible.
fuente
command1 &> /dev/null || echo "command1 borked it"
command1 || (echo command1 borked it ; exit)
Una alternativa es simplemente unir los comandos
&&
para que el primero en fallar evite que el resto se ejecute:Esta no es la sintaxis que solicitó en la pregunta, pero es un patrón común para el caso de uso que describe. En general, los comandos deben ser responsables de los errores de impresión para que no tenga que hacerlo manualmente (tal vez con una
-q
bandera para silenciar los errores cuando no los desee). Si tiene la capacidad de modificar estos comandos, los editaría para gritar en caso de falla, en lugar de envolverlos en otra cosa que lo haga.Tenga en cuenta también que no necesita hacer:
Simplemente puedes decir:
Y cuando se hace necesario comprobar los códigos de retorno utilizan un contexto en lugar de la aritmética
[ ... -ne
:fuente
En lugar de crear funciones de corredor o usar
set -e
, use untrap
:La trampa incluso tiene acceso al número de línea y a la línea de comando del comando que lo activó. Las variables son
$BASH_LINENO
y$BASH_COMMAND
.fuente
trap - ERR
para apagar la trampa al final del "bloque".Personalmente, prefiero utilizar un enfoque ligero, como se ve aquí ;
Ejemplo de uso:
fuente
fuente
$*
, fallará si algún argumento tiene espacios en ellos; utilizar"$@"
en su lugar. (Aunque $ * está bien en elecho
comando.)Desarrollé una implementación de prueba y captura casi perfecta en bash, que le permite escribir código como:
¡Incluso puedes anidar los bloques try-catch dentro de ellos!
El código es parte de mi bash boilerplate / framework . Extiende aún más la idea de probar y atrapar con cosas como el manejo de errores con retroceso y excepciones (además de algunas otras características interesantes).
Aquí está el código que es responsable solo de try & catch:
Siéntase libre de usar, bifurcar y contribuir: está en GitHub .
fuente
Lo siento, no puedo hacer un comentario a la primera respuesta, pero debe usar una nueva instancia para ejecutar el comando: cmd_output = $ ($ @)
fuente
Para los usuarios de conchas de pescado que tropiezan con este hilo.
Sea
foo
una función que no "devuelve" (echo) un valor, pero establece el código de salida como de costumbre.Para evitar verificar
$status
después de llamar a la función, puede hacer lo siguiente:Y si es demasiado largo para caber en una línea:
fuente
Cuando lo uso
ssh
, necesito distinguir entre los problemas causados por problemas de conexión y los códigos de error del comando remoto en modoerrexit
(set -e
). Yo uso la siguiente función:fuente
Puede usar la increíble solución de @john-kugelman que se encuentra arriba en los sistemas que no son RedHat comentando esta línea en su código:
Luego, pegue el siguiente código al final. Divulgación completa: esto es solo una copia directa y pegar los bits relevantes del archivo mencionado anteriormente tomado de Centos 7.
Probado en MacOS y Ubuntu 18.04.
fuente
Comprobación del estado de manera funcional
Uso:
Salida
fuente