我想在shell脚本中编写逻辑,如果由于某种原因而失败,它将根据“状态码=失败”重试15秒后最多5次再次运行,最多5次。

#1 楼

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

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


评论


如果命令成功,您应该添加break,它将破坏循环

–拉胡尔·帕蒂尔(Rahul Patil)
13年7月11日在7:39

实际上,正确的写法是if命令;然后休息fi或更简洁地命令&& break

–tripleee
13年7月11日在8:05

你能在这里解释命令吗?这是否意味着运行脚本的命令

– Sandeep Singh
13年7月11日在12:45

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

–嫌疑犯
13年7月11日在13:00

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

–mattdm
2015年12月23日14:20在

#2 楼

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


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


变体:

使用{..}语法。适用于大多数外壳,但不适用于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 


评论


+1-简洁明了。一个建议:我将用{1..5}中的i代替1 2 3 4 5中的i,因为它更易于维护。

–帕迪·兰道(Paddy Landau)
13年7月11日在13:03



请注意,这是有效的,因为&&是在||之前求值的由于运算符优先级

–gene_wood
15年9月4日在19:50

另请注意,即使命令失败,它也会返回代码0。

– Henrique Zambon
17年5月26日在15:34

@HenriqueZambon添加了一个可以处理该问题的版本。

–亚历山大
17年7月10日在13:14

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

–戴夫·拉格
18年1月15日在18:46



#3 楼

function fail {
  echo  >&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


用于包含复杂命令的真实示例,请参见此脚本。

评论


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

– Ben Liyanage
17年2月23日在0:44

#4 楼

GNU Parallel具有--retries

parallel --retries 5 --delay 15s ::: ./do_thing.sh


示例:

parallel -t --retries 5 --delay 0.1s 'echo {};exit {}' ::: {0..10}


评论


这行不通。 --retries用于在不同的机器上重试:“如果作业失败,请在另一台没有失败的计算机上重试。执行n次。如果--sshlogin中的计算机少于n台,则GNU并行将重新使用所有计算机。如果某些作业没有明显原因失败(例如网络故障),这将很有用”

–詹姆斯·摩尔
20年5月8日在16:31

你测试了吗--retries适用于本地和远程作业。但是对于远程作业,GNU Parallel尝试在可能的情况下在另一台服务器上重试该作业(也许由于某种未知原因,该作业和该服务器彼此不喜欢)。

–奥雷·丹吉(Ole Tange)
20年5月8日在16:45

(顺便说一句,并行是一个了不起的工具。)

–詹姆斯·摩尔
20年5月8日在17:41

事实证明,我对重试的文档和一个未记录的功能(至少我没有看到文档)感到困惑-如果失败,并且重试已打开,则直到最后一个致命错误之前,您都看不到stderr。

–詹姆斯·摩尔
20年5月8日在18:07

并行的--help可能更好。总的来说,它是一个开发完善的工具,肯定会比其他替代品略胜一筹。 (至少因为引文nagscreen不见了,所以对我来说这是不行的。)顺便说一句:--delay不仅延迟重试,还延迟了所有操作。 afaik尚无功能可仅在出错时延迟(将很有用

–约翰
5小时前

#5 楼

这是重试功能

function retry()
{
        local n=0
        local try=
        local cmd="${@: 2}"
        [[ $# -le 1 ]] && {
        echo "Usage 
[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_number> <Command>"; } until [[ $n -ge $try ]] do $cmd && break || { echo "Command Fail.." ((n++)) echo "retry $n ::" sleep 1; } done } retry $*


输出:

q4312078q

评论


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

–java_enthu
13年8月19日在10:54

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

–拉胡尔·帕蒂尔(Rahul Patil)
13年8月19日在11:26

是的,Rahul我确实尝试过。

–java_enthu
13年8月19日在11:29

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

–拉胡尔·帕蒂尔(Rahul Patil)
13年8月19日在12:35

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

–杰里·安德鲁斯(Jerry Andrews)
19年5月8日在17:42

#6 楼

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

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


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

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


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

评论


在zsh中,使用fc -e“#”代替fc -s。

–里卡多·斯图文(Ricardo Stuven)
17年8月17日在14:55

太酷了! @RicardoStuven感谢您使用fc -e“#”-它也可以在bash中使用。

– Noam Manos
20-4-28在10:15



#7 楼

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

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


每15秒将执行一次操作,直到成功为止,最多五次。 br />

#8 楼

参见下面的示例:

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

done


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

$?如果为零表示命令成功运行,则存在命令状态,如果不是零则表示命令fai

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

评论


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

– Sandeep Singh
13年7月11日在7:28

请立即检查,我已更新

–拉胡尔·帕蒂尔(Rahul Patil)
13年7月11日在7:32

$?如果为零则表示命令成功运行,如果为零则表示命令失败

–拉胡尔·帕蒂尔(Rahul Patil)
13年7月11日在7:34

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

– Sandeep Singh
13年7月11日在7:43



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

–拉胡尔·帕蒂尔(Rahul Patil)
13年7月11日在7:44



#9 楼

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

#!/usr/bin/env bash

if [ $# -ne 3 ]; then
    echo 'usage: retry <num retries> <wait retry secs> "<command>"'
    exit 1
fi

retries=
wait_retry=
command=

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
17年1月6日在12:43

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

–yo.ian.g
17年1月6日在12:49

如果像mysql -uroot -pxxxx -e'DROP DATABASE xxx'这样的cmd不起作用

–wyx
20年7月8日在2:39



尝试将您的cmd

– Padilo
20年7月8日在15:00

#10 楼

需要多次执行此操作,脚本无法使用,因此我为此创建了一个专用工具,称为重试。

retry --until=success --times=5 --delay=15 command ...


重试在这里可用: https://github.com/minfrin/retry

评论


我喜欢该工具如何意识到它可能在希望命令仅运行一次的管道中运行。做得好。

–韦恩·康拉德(Wayne Conrad)
20年5月7日在13:21

#11 楼

这是一个用于函数式编程的纯粹主义者的递归retry函数:像这样:

retry() {
  cmd=
  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 "" ] && 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
}


评论


这对于长度超过一个字的命令不起作用:cmd =“ echo blah blah” ...第10行:[:blah:预期为整数表达式...也不适用于管道等。

–Mercury00
19年3月14日在18:05



#12 楼

由于现有答案无法回答这个问题,


不抛出错误代码。
通过执行exit errCode,Bash无法兑现某些陷阱,例如trap somefunc ERR


 COMMAND="SOMECOMMAND"
TOTAL_RETRIES=3

retrycount=0
until [ $retrycount -ge $((TOTAL_RETRIES-1)) ]
do
   $COMMAND && break
   retrycount=$((retrycount+1))
   sleep 1
done

if [ $retrycount -eq $((TOTAL_RETRIES-1)) ]
then
    $COMMAND
fi
 


#13 楼

    # Retries a given command given number of times and outputs to given variable
    #  : Command to be passed : handles both simple, piped commands
    #  : Final output of the command(if successfull)
    #  : Number of retrial attempts[Default 5]
    function retry_function() {
        echo "Command to be executed : "
        echo "Final output variable : "
        echo "Total trials [Default:5] : "
        counter=${3:-5}
        local _my_output_= #make sure passed variable is not same as this
        i=1
        while [ $i -le $counter ]; do
            local my_result=$(eval "")
            # this tests if output variable is populated and accordingly retries,
            # Not possible to provide error status/logs(STDIN,STDERR)-owing to subshell execution of command
            # if error logs are needed, execute the same code, outside function in same shell
            if test -z "$my_result"
            then
                echo "Trial[$i/$counter]: Execution failed"
            else
                echo "Trial[$i/$counter]: Successfull execution"
                eval $_my_output_="'$my_result'"
                break
            fi
            let i+=1
        done
    }

    retry_function "ping -c 4 google.com | grep \"min/avg/max\" | awk -F\"/\" '{print $5}'" avg_rtt_time
    echo $avg_rtt_time

 - To pass in a lengthy command, pass a method echoing the content. Take care of method expansion accordingly in a subshell at appropriate place.
 - Wait time can be added too - just before the increment!
 - For a complex command, youll have to take care of stringifying it(Good luck)


#14 楼

这是一个老问题,但我发现自己经常回到这个问题上。我的用例是使用一个内衬,该内衬可以重试命令一次,最多可以与kubernetes pod一起使用n次(当然,如果它适用于bash脚本)。
 TRY=6; until [ $TRY -eq 0 ] || <your command over here> ; do echo $TRY; echo "<output message>"; TRY=$(expr $TRY - 1); sleep 15; done;
 

一根内衬很难让人理解,但它会非常有帮助。