Quiero escribir lógica en el script de shell que volverá a intentar ejecutarlo después de 15 segundos hasta 5 veces según "código de estado = FALLO" si falla debido a algún problema.
Quiero escribir lógica en el script de shell que volverá a intentar ejecutarlo después de 15 segundos hasta 5 veces según "código de estado = FALLO" si falla debido a algún problema.
Respuestas:
Este script usa un contador n
para limitar los intentos del comando a cinco. Si el comando tiene éxito, $?
mantendrá cero y la ejecución se interrumpirá del bucle.
n=0
until [ $n -ge 5 ]
do
command && break # substitute your command here
n=$[$n+1]
sleep 15
done
if command; then break; fi
o más sucintamente solocommand && break
n
fallas, duerme innecesariamente una vez más antes de salir.
for i in 1 2 3 4 5; do command && break || sleep 15; done
Reemplace "comando" con su comando. Esto supone que "código de estado = FALLO" significa cualquier código de retorno distinto de cero.
Usando la {..}
sintaxis. Funciona en la mayoría de los shells, pero no en BusyBox sh
:
for i in {1..5}; do command && break || sleep 15; done
Usando seq
y pasando el código de salida del comando fallido:
for i in $(seq 1 5); do command && s=0 && break || s=$? && sleep 15; done; (exit $s)
Igual que el anterior, pero omitiendo sleep 15
después del fallo final. Dado que es mejor definir solo el número máximo de bucles una vez, esto se logra durmiendo al comienzo del bucle si i > 1
:
for i in $(seq 1 5); do [ $i -gt 1 ] && sleep 15; command && s=0 && break || s=$?; done; (exit $s)
for i in 1 2 3 4 5
con for i in {1..5}
porque es más fácil de mantener.
command
falla.
[[ i -eq 5]]
como una condición OR antes del sueño para evitar esto.
function fail {
echo $1 >&2
exit 1
}
function retry {
local n=1
local max=5
local delay=15
while true; do
"$@" && break || {
if [[ $n -lt $max ]]; then
((n++))
echo "Command failed. Attempt $n/$max:"
sleep $delay;
else
fail "The command has failed after $n attempts."
fi
}
done
}
Ejemplo:
retry ping invalidserver
produce esta salida:
ping: unknown host invalidserver
Command failed. Attempt 2/5:
ping: unknown host invalidserver
Command failed. Attempt 3/5:
ping: unknown host invalidserver
Command failed. Attempt 4/5:
ping: unknown host invalidserver
Command failed. Attempt 5/5:
ping: unknown host invalidserver
The command 'ping invalidserver' failed after 5 attempts
Para ver un ejemplo práctico del mundo real con comandos complejos, consulte este script .
Aquí está la función para reintentar
function retry()
{
local n=0
local try=$1
local cmd="${@: 2}"
[[ $# -le 1 ]] && {
echo "Usage $0 <retry_number> <Command>"; }
until [[ $n -ge $try ]]
do
$cmd && break || {
echo "Command Fail.."
((n++))
echo "retry $n ::"
sleep 1;
}
done
}
retry $*
Salida:
[test@Nagios ~]$ ./retry.sh 3 ping -c1 localhost
PING localhost (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.207 ms
--- localhost ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.207/0.207/0.207/0.000 ms
[test@Nagios ~]$ ./retry.sh 3 ping -c1 localhostlasjflasd
ping: unknown host localhostlasjflasd
Command Fail..
retry 1 ::
ping: unknown host localhostlasjflasd
Command Fail..
retry 2 ::
ping: unknown host localhostlasjflasd
Command Fail..
retry 3 ::
bash retry.sh 3 ping -c1 localhost
Aquí está mi alias / script favorito de una línea
alias retry='while [ $? -ne 0 ] ; do fc -s ; done'
Entonces puedes hacer cosas como:
$ ps -ef | grep "Next Process"
$ retry
y seguirá ejecutando el comando anterior hasta que encuentre "Siguiente proceso"
fc -e "#"
lugar de fc -s
.
Utilizo este script que realiza los reintentos de un comando dado, el beneficio de este script es que si falla todos los reintentos conservará el código de salida.
#!/usr/bin/env bash
if [ $# -ne 3 ]; then
echo 'usage: retry <num retries> <wait retry secs> "<command>"'
exit 1
fi
retries=$1
wait_retry=$2
command=$3
for i in `seq 1 $retries`; do
echo "$command"
$command
ret_value=$?
[ $ret_value -eq 0 ] && break
echo "> failed with $ret_value, waiting to retry..."
sleep $wait_retry
done
exit $ret_value
Probablemente puede ser más simple
Ver abajo Ejemplo:
n=0
while :
do
nc -vzw1 localhost 3859
[[ $? = 0 ]] && break || ((n++))
(( n >= 5 )) && break
done
Estoy tratando de conectar el puerto 3389 en localhost, volverá a intentarlo hasta que falle 5 veces, si tiene éxito, romperá el ciclo.
$?
es el estado del comando si cero significa que el comando se ejecutó con éxito, si otro que cero significa comando fai
Parece un poco complicado, puede ser que alguien lo haga mejor que esto.
$?
es el estado del comando si cero significa que el comando se ejecutó correctamente, si otro que cero significa que el comando falla
Puede usar el loop
comando, disponible aquí , así:
$ loop './do_thing.sh' --every 15s --until-success --num 5
Que hará lo suyo cada 15 segundos hasta que tenga éxito, por un máximo de cinco veces.
Aquí hay una retry
función recursiva para puristas de programación funcional:
retry() {
cmd=$1
try=${2:-15} # 15 by default
sleep_time=${3:-3} # 3 seconds by default
# Show help if a command to retry is not specified.
[ -z "$1" ] && echo 'Usage: retry cmd [try=15 sleep_time=3]' && return 1
# The unsuccessful recursion termination condition (if no retries left)
[ $try -lt 1 ] && echo 'All retries failed.' && return 1
# The successful recursion termination condition (if the function succeeded)
$cmd && return 0
echo "Execution of '$cmd' failed."
# Inform that all is not lost if at least one more retry is available.
# $attempts include current try, so tries left is $attempts-1.
if [ $((try-1)) -gt 0 ]; then
echo "There are still $((try-1)) retrie(s) left."
echo "Waiting for $sleep_time seconds..." && sleep $sleep_time
fi
# Recurse
retry $cmd $((try-1)) $sleep_time
}
Páselo un comando (o un nombre de función) y, opcionalmente, una serie de reintentos y una duración de suspensión entre reintentos, de esta manera:
retry some_command_or_fn 5 15 # 5 tries, sleep 15 seconds between each
break
si el comando tiene éxito, entonces se romperá el ciclo