
Tengo un script bash que se utiliza set -o errexit
para que, en caso de error, todo el script salga en el punto de falla.
El script ejecuta un curl
comando que a veces no puede recuperar el archivo deseado; sin embargo, cuando esto ocurre, el script no sale por error.
He añadido un for
bucle a
- haga una pausa durante unos segundos y luego vuelva a intentar el
curl
comando - utilícelo
false
en la parte inferior del bucle for para definir un estado de salida predeterminado distinto de cero; si el comando curl tiene éxito, el bucle se rompe y el estado de salida del último comando debe ser cero.
#! /bin/bash
set -o errexit
# ...
for (( i=1; i<5; i++ ))
do
echo "attempt number: "$i
curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim
if [ -f ~/.vim/autoload/pathogen.vim ]
then
echo "file has been retrieved by curl, so breaking now..."
break;
fi
echo "curl'ed file doesn't yet exist, so now will wait 5 seconds and retry"
sleep 5
# exit with non-zero status so main script will errexit
false
done
# rest of script .....
El problema es que cuando el curl
comando falla, el bucle reintenta el comando cinco veces; si todos los intentos no tienen éxito, el bucle for finaliza y se reanuda el script principal, en lugar de activar el archivo errexit
.
¿Cómo puedo hacer que se cierre todo el script si esta curl
declaración falla?
Respuesta1
Reemplazar:
done
con:
done || exit 1
Esto hará que el código salga si el for
bucle sale con un código de salida distinto de cero.
Como curiosidad, la 1
entrada exit 1
no es necesaria. Un comando simple exit
saldría con el estado de salida del último comando ejecutado, que sería false
(código=1) si falla la descarga. Si la descarga se realiza correctamente, el código de salida del bucle es el código de salida del echo
comando. echo
normalmente sale con código = 0, lo que indica éxito. En ese caso, ||
no se activa y el exit
comando no se ejecuta.
Por último, tenga en cuenta que set -o errexit
puede estar lleno de sorpresas. Para una discusión de sus pros y contras, verPreguntas frecuentes #105 de Greg.
Documentación
De man bash
:
para((expr1; expr2; expr3));hacerlista ; hecho
Primero, la expresión aritmética expr1 se evalúa de acuerdo con las reglas que se describen a continuación en EVALUACIÓN ARITMÉTICA. Luego, la expresión aritmética expr2 se evalúa repetidamente hasta que da como resultado cero. Cada vez que expr2 se evalúa como un valor distinto de cero, se ejecuta list y se evalúa la expresión aritmética expr3. Si se omite alguna expresión, se comporta como si su evaluación fuera 1. El valor de retorno es el estado de salida del último comando de la lista que se ejecuta, o falso si alguna de las expresiones no es válida.[Énfasis añadido]
Respuesta2
Si prefiere detener el ciclo en el primer fallo y también evitarlo set -e
, puede hacer algo como esto:
for i in `seq 1 10`; do
run_command || exit 1;
done
Respuesta3
Si lo ha errexit
configurado, entonces la false
declaración debería hacer que el script se cierre inmediatamente. Lo mismo si el curl
comando falla.
Su script de ejemplo, tal como está escrito, debería salir después del primer curl
error del comando la primera vez que llama false
si se establece errexit.
Para ver cómo funciona (uso la abreviatura -e
de configurar errexit
:
$ ( set -e; false; echo still here )
$
$ ( set +e; false; echo still here )
still here
$
Entonces, si el curl
comando se ejecuta más de una vez, este script no está errexit
configurado.
Respuesta4
set -o errexit
Puede ser complicado en bucles y subcapas, porque hay que volver a salir del proceso.
Romper un bucle (incluso en funcionamiento normal) se considera una mala práctica. Puedes llamarme de la vieja escuela para preferir un bucle while en lugar de un bucle for para dos condiciones, pero me parece mejor leer:
i=1
RET=-1
while [ $i -le 5 ] && [ $RET -ne 0 ]; do
[ $i -eq 1 ] || sleep 5
echo "attempt number: "$i
curl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim
RET=$?
i=$((i+1))
done
exit $RET