programacion - scripts linux ejercicios resueltos
¿Anulando un script de shell si algún comando devuelve un valor distinto de cero? (9)
Tengo un script de shell Bash que invoca una serie de comandos. Me gustaría que el script de shell salga automáticamente con un valor de retorno de 1 si alguno de los comandos devuelve un valor distinto de cero.
¿Es esto posible sin verificar explícitamente el resultado de cada comando?
p.ej
dosomething1
if [[ $? -ne 0 ]]; then
exit 1
fi
dosomething2
if [[ $? -ne 0 ]]; then
exit 1
fi
Agregue esto al comienzo del script:
set -e
Esto hará que el shell se cierre inmediatamente si un comando simple sale con un valor de salida distinto de cero. Un comando simple es cualquier comando que no sea parte de if, while, o hasta prueba, o parte de && o || lista.
Consulte la página del manual de bash (1) en el comando interno "set" para obtener más detalles.
Personalmente comienzo casi todos los scripts de shell con "set -e". Es realmente molesto que un script continúe obstinadamente cuando algo falla en el medio y rompe las suposiciones para el resto del script.
Ejecutar con -e
o set -e
en la parte superior.
También mira set -u
.
El $?
la variable rara vez es necesaria. El command; if [ $? -eq 0 ]; then X; fi
pseudo-idiomático command; if [ $? -eq 0 ]; then X; fi
command; if [ $? -eq 0 ]; then X; fi
command; if [ $? -eq 0 ]; then X; fi
siempre debe escribirse como if command; then X; fi
if command; then X; fi
if command; then X; fi
.
Los casos donde $?
se requiere cuando se debe verificar contra varios valores:
command
case $? in
(0) X;;
(1) Y;;
(2) Z;;
esac
o cuando $?
necesita ser reutilizado o manipulado de otra manera:
if command; then
echo "command successful" >&2
else
ret=$?
echo "command failed with exit code $ret" >&2
exit $ret
fi
Las declaraciones if en tu ejemplo son innecesarias. Solo hazlo así:
dosomething1 || exit 1
Si sigue el consejo de Ville Laurikari y usa set -e
entonces para algunos comandos puede necesitar usar esto:
dosomething || true
El || true
|| true
hará que la canalización del comando tenga un valor de retorno true
, incluso si el comando falla, por lo que la opción -e
no eliminará la secuencia de comandos.
Para añadir a la respuesta aceptada:
Tenga en cuenta que set -e
veces no es suficiente, especialmente si tiene tuberías.
Por ejemplo, supongamos que tiene este script
#!/bin/bash
set -e
./configure > configure.log
make
... que funciona como se esperaba: un error en la configure
anula la ejecución.
Mañana haces un cambio aparentemente trivial:
#!/bin/bash
set -e
./configure | tee configure.log
make
... y ahora no funciona. Esto se explica here , y se proporciona una solución alternativa (solo Bash):
#!/bin/bash set -e set -o pipefail ./configure | tee configure.log make
Si tiene una limpieza que necesita hacer al salir, también puede usar ''trampa'' con el ERR de pseudo-señal. Esto funciona de la misma manera que la captura de INT o cualquier otra señal; bash lanza ERR si algún comando sale con un valor distinto de cero:
# Create the trap with
# trap COMMAND SIGNAME [SIGNAME2 SIGNAME3...]
trap "rm -f /tmp/$MYTMPFILE; exit 1" ERR INT TERM
command1
command2
command3
# Partially turn off the trap.
trap - ERR
# Now a control-C will still cause cleanup, but
# a nonzero exit code won''t:
ps aux | grep blahblahblah
O, especialmente si está utilizando "set -e", podría interceptar EXIT; su captura se ejecutará cuando el script salga por cualquier motivo, incluido un final normal, interrupciones, una salida causada por la opción -e, etc.
Una expresión como
dosomething1 && dosomething2 && dosomething3
detendrá el procesamiento cuando uno de los comandos regrese con un valor distinto de cero. Por ejemplo, el siguiente comando nunca se imprimirá "hecho":
cat nosuchfile && echo "done"
echo $?
1
solo agregue otra como referencia, ya que hubo una pregunta adicional a la entrada de Mark Edgars y aquí hay un ejemplo adicional y toca el tema en general:
[[ `cmd` ]] && echo success_else_silence
que es lo mismo que cmd || exit errcode
cmd || exit errcode
como alguien mostró.
p.ej. Quiero asegurarme de que una partición está desmontada si está montada:
[[ `mount | grep /dev/sda1` ]] && umount /dev/sda1
#!/bin/bash -e
debería bastar