如何在脚本中编写重试逻辑以使重试逻辑最多运行5次?


Answers:


89

该脚本使用计数器n将命令尝试次数限制为五次。如果命令成功$?执行,将保持零,执行将从循环中止。

n=0
until [ $n -ge 5 ]
do
   command && break  # substitute your command here
   n=$[$n+1]
   sleep 15
done

1
您应该添加break命令是否成功,则它将中断循环
Rahul Patil

实际上,正确的写作方式if command; then break; fi或更简明地是这样command && break
Tripleee

1
“命令”只是您要检查其状态的命令的名称。
嫌疑犯

3
值得一提的是,您可以在最后测试n是否等于5,以了解命令是否成功。
mattdm 2015年

4
很好的解决方案-但是在n出现故障的情况下,它在退出之前不必要地再睡一会儿。
罗恩·罗斯曼

124
for i in 1 2 3 4 5; do command && break || sleep 15; done

用您的命令替换“命令”。假设“状态码=失败”表示任何非零返回码。


变化:

使用{..}语法。适用于大多数shell,但不适用于BusyBox sh

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

使用seq并传递失败命令的退出代码:

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

与上述相同,但sleep 15在最终失败后跳过。由于最好只定义一次最大循环数,因此可以通过以下方式在循环开始时休眠来实现i > 1

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

25
+1-简洁明了。一个建议:我将替换for i in 1 2 3 4 5for i in {1..5}它,因为它更易于维护。
Paddy Landau

5
请注意,这之所以&&||
行之有效

6
另请注意,即使command失败,它也会返回代码0 。
Henrique Zambon

3
@HenriqueZambon添加了一个可以处理该问题的版本。
亚历山大

2
最终失败后,这不睡觉吗?似乎不必要的15s等待。我认为您可以[[ i -eq 5]]在睡前检查是否为OR条件,以避免这种情况。
Dave Lugg

32
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
}

例:

retry ping invalidserver

产生以下输出:

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

有关使用复杂命令的真实示例,请参见此脚本


3
这是一个很好的解决方案。我喜欢它在多次失败后也以非零退出状态退出。
Ben Liyanage

11

这是重试功能

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

输出:

[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 ::

我将您的代码复制粘贴到名为retry.sh的新文件中,并在顶部添加#!/ bin / bash行。在解释中使用给定命令运行时,我什么也没看到,只是提示再次出现。
java_enthu 2013年

您是否尝试过bash retry.sh 3 ping -c1 localhost
Rahul Patil

是的,Rahul我确实尝试过。
java_enthu

抱歉,我很着迷。.,我再次测试过,它正常工作,请检查输出paste.ubuntu.com/6002711
Rahul Patil

到目前为止,这是迄今为止最优雅的答案-如果您要进行的操作很简单。感谢您抽出宝贵的时间。
杰里·安德鲁斯


5

这是我最喜欢的一行别名/脚本

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

然后,您可以执行以下操作:

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

并且它将继续运行先前的命令,直到找到“下一个进程”


1
在zsh中,使用fc -e "#"代替fc -s
里卡多·斯图文

2

我使用该脚本重试给定命令,此脚本的好处是,如果所有重试均失败,它将保留退出代码。

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

可能会变得更简单


我喜欢这个版本的灵活性,以及​​代码的详细程度和可读性!
yo.ian.g

要匹配失败的回声,您甚至可以使用[$ ret_value -eq 0]添加成功的回声,或者之后再测试$ ret_value
yo.ian.g

此版本的优点是命令最后一次失败后不休眠。
亚历山大

1

参见以下示例:

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

done

我正在尝试在本地主机上连接端口3389,它将重试直到5次失败,如果成功,则它将中断循环。

$? 如果为零表示命令成功运行,则为命令的存在状态;如果为零,则表示命令成功。

似乎有点复杂,也许有人做得比这更好。


谢谢rahul ..将继续尝试运行脚本吗?
Sandeep Singh

请立即检查,我已经更新了
Rahul Patil

$?如果为零表示命令成功运行,则为命令的存在状态;如果为零,则表示命令失败
Rahul Patil

是否需要提供主机和端口地址。我们可以通过仅提供脚本位置目录来做到这一点吗?
Sandeep Singh

用给出退出状态代码$?的任何命令替换
Rahul Patil

1

您可以使用此处loop提供的命令,如下所示:

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

这将每15秒执行一次您的操作,直到成功为止,最多五次。


0

这是retry函数式编程纯粹主义者的递归函数:

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
}

向其传递命令(或函数名称),还可以向其传递重试次数和重试之间的睡眠持续时间,例如:

retry some_command_or_fn 5 15 # 5 tries, sleep 15 seconds between each

整数表达预期...它也不管道工作,等等:CMD =“回波等等等等” ...线10:[:嗒嗒这并不适用于命令多于一个字长工作
Mercury00
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.