Como escrevo uma lógica de repetição no script para continuar tentando executá-la até 5 vezes?

86

Eu quero escrever a lógica no shell script que tentará novamente para executar novamente após 15 segundos até 5 vezes com base em "código de status = FAIL" se falhar devido para algum problema.

    
por Sandeep Singh 11.07.2013 / 09:07

10 respostas

67

Este script usa um contador n para limitar as tentativas do comando a cinco. Se o comando for bem-sucedido, $? será zerado e a execução será interrompida no loop.

   n=0
   until [ $n -ge 5 ]
   do
      command && break  # substitute your command here
      n=$[$n+1]
      sleep 15
   done
    
por 11.07.2013 / 09:38
102
for i in 1 2 3 4 5; do command && break || sleep 15; done

Substitua "comando" pelo seu comando. Isso está assumindo que "código de status = FAIL" significa qualquer código de retorno diferente de zero.

Variações:

Usando a sintaxe {..} . Funciona na maioria dos shells, mas não no BusyBox sh :

for i in {1..5}; do command && break || sleep 15; done

Usando seq e passando o código de saída do comando com falha:

for i in $(seq 1 5); do command && s=0 && break || s=$? && sleep 15; done; (exit $s)

O mesmo que acima, mas pulando sleep 15 após a falha final. Como é melhor definir apenas o número máximo de loops uma vez, isso é obtido dormindo no início do loop se i > 1 :

for i in $(seq 1 5); do [ $i -gt 1 ] && sleep 15; command && s=0 && break || s=$?; done; (exit $s)
    
por 11.07.2013 / 11:32
22
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
}

Exemplo:

retry ping invalidserver

produz esta saída:

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 um exemplo prático de trabalho real com comandos complexos, consulte este script .

    
por 17.06.2014 / 16:52
9

Aqui está a função para tentar novamente

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 $*

Saída:

[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 ::
    
por 11.07.2013 / 12:03
3

O GNU Parallel tem --retries :

parallel --retries 5 --delay 15s ::: ./do_thing.sh
    
por 25.09.2018 / 22:36
2

Aqui está o meu alias / script de uma linha favorita

    alias retry='while [ $? -ne 0 ] ; do fc -s ; done'

Então você pode fazer coisas como:

     $ ps -ef | grep "Next Process"
     $ retry

e continuará a executar o comando anterior até encontrar o "Próximo processo"

    
por 03.12.2015 / 17:15
2

Eu uso este script que faz as tentativas de um determinado comando, o benefício deste script é que, se falhar todas as tentativas, preservará o código de saída.

#!/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

Provavelmente, pode ficar mais simples

    
por 01.12.2016 / 13:00
1

Veja abaixo Exemplo:

n=0
while :
do
        nc -vzw1 localhost 3859
        [[ $? = 0 ]] && break || ((n++))
        (( n >= 5 )) && break

done

Eu estou tentando conectar a porta 3389 no host local, ele tentará até 5 vezes falhar, se o sucesso for quebrar o loop.

$? existe o status do comando se zero significa comando executado com sucesso, se diferente de zero significa comando fai

Parece um pouco complicado, pode ser que alguém faça melhor que isso.

    
por 11.07.2013 / 09:19
1

Você pode usar o comando loop , disponível aqui , assim:

$ loop './do_thing.sh' --every 15s --until-success --num 5 

Que fará o que quiser a cada 15 segundos até que tenha sucesso, por no máximo cinco vezes.

    
por 26.06.2018 / 23:00
0

Aqui está uma função retry recursiva para puristas de programação 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
}

Passe um comando (ou um nome de função) e, opcionalmente, um número de tentativas e uma duração de sono entre novas tentativas, da seguinte forma:

retry some_command_or_fn 5 15 # 5 tries, sleep 15 seconds between each
    
por 23.02.2018 / 12:16