如何在 Bash 中重试命令?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7449772/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 00:48:04  来源:igfitidea点击:

How to retry a command in Bash?

bash

提问by stevejb

I have a command that should take less than 1 minute to execute, but for some reason has an extremely long built-in timeout mechanism. I want some bash that does the following:

我有一个命令执行时间应该不到 1 分钟,但由于某种原因,它有一个非常长的内置超时机制。我想要一些执行以下操作的 bash:

success = False

try(my_command)

while(!(success))
wait 1 min
if my command not finished
     retry(my_command)
else
     success = True   
end while

How can I do this in Bash?

我怎样才能在 Bash 中做到这一点?

回答by Jonathan Leffler

Look at the GNU timeoutcommand. This kills the process if it has not completed in a given time; you'd simply wrap a loop around this to wait for the timeoutto complete successfully, with delays between retries as appropriate, etc.

查看 GNUtimeout命令。如果在给定时间内没有完成,这将终止进程;您只需在此周围环绕一个循环以等待timeout成功完成,并在重试之间适当延迟等。

while timeout -k 70 60 -- my_command; [ $? = 124 ]
do sleep 2  # Pause before retry
done

If you must do it in pure bash(which is not really feasible - bashuses lots of other commands), then you are in for a world of pain and frustration with signal handlers and all sorts of issues.

如果您必须纯粹地执行此操作bash(这实际上并不可行 -bash使用许多其他命令),那么您将面临信号处理程序和各种问题的痛苦和沮丧。



Please expand on your answer a little. -k 70is --kill-after= 70seconds, 124 exit on timeout; what is the 60?

请稍微扩展您的答案。-k 70--kill-after= 70秒,124 超时退出;什么是60?

The linked documentation does explain the command; I don't really plan to repeat it all here. The synopsis is timeout [options] duration command [arg]...; one of the options is -k duration. The -k durationsays "if the command does not die after the SIGTERM signal is sent at 60 seconds, send a SIGKILL signal at 70 seconds" (and the command should die then). There are a number of documented exit statuses; 124 indicates that the command timed out; 137 that it died after being sent the SIGKILL signal, and so on. You can't tell if the command itself exits with one of the documented statuses.

链接的文档确实解释了命令;我真的不打算在这里重复这一切。概要是timeout [options] duration command [arg]...; 选项之一是-k duration。在-k duration说“如果SIGTERM信号在60秒后发送的命令不会死亡,在70秒发送SIGKILL信号”(以及命令应该然后死亡)。有许多记录在案的退出状态;124 表示命令超时;137 它在发送 SIGKILL 信号后死亡,依此类推。您无法判断命令本身是否以记录的状态之一退出。

回答by rhinoceros.xn

I found a script from: http://fahdshariff.blogspot.com/2014/02/retrying-commands-in-shell-scripts.html

我从以下位置找到了一个脚本:http: //fahdshariff.blogspot.com/2014/02/retrying-commands-in-shell-scripts.html

#!/bin/bash

# Retries a command on failure.
#  - the max number of attempts
# ... - the command to run

retry() {
    local -r -i max_attempts=""; shift
    local -r cmd="$@"
    local -i attempt_num=1
    until $cmd
    do
        if ((attempt_num==max_attempts))
        then
            echo "Attempt $attempt_num failed and there are no more attempts left!"
            return 1
        else
            echo "Attempt $attempt_num failed! Trying again in $attempt_num seconds..."
            sleep $((attempt_num++))
        fi
    done
}

# example usage:
retry 5 ls -ltr foo

回答by tripleee

Adapting @Shin's answer to use kill -0rather than jobsso that this should work even with classic Bourne shell, and allow for other background jobs. You may have to experiment with killand waitdepending on how my_commandresponds to those.

调整@Shin 的答案以使用kill -0而不是jobs这样,即使使用经典的 Bourne shell 也能正常工作,并允许其他后台作业。您可能需要进行试验,killwait取决于对这些的my_command反应。

while true ; do
    my_command &
    sleep 60
    if kill -0 $! 2>/dev/null; then
        # Job took too long
        kill $!
    else
        echo "Job is done"
        # Reap exit status
        wait $!
        break
    fi
done

回答by analogue

I liked @Jonathan's answer, but tried to make it more straight forward for future use:

我喜欢@Jonathan 的回答,但试图让它更直接以备将来使用:

until timeout 1 sleep 2
do
    echo "Happening after 1s of sleep"
done

回答by Kilian Foth

You can run a command and retain control with the &background operator. Run your command in the background, sleepfor as long as you wish in the foreground, and then, if the background job hasn't terminated, kill it and start over.

您可以运行命令并保留&后台操作员的控制权。在后台运行您的命令,sleep只要您希望在前台运行,然后,如果后台作业尚未终止,请终止它并重新开始。

回答by Shizzmo

 while true ; do
     my_command &
     sleep 60
     if [[ $(jobs -r) == "" ]] ; then
         echo "Job is done"
         break
     fi
     # Job took too long
     kill -9 $!
 done

回答by J0hnG4lt

Using the "retrycli" tool you can get exponential backoff and retries on any bash script or tool (not bash functions though, unless they have been moved into another script which is then called by retry).

使用“retrycli”工具,您可以获得指数退避并在任何 bash 脚本或工具上重试(但不是 bash 函数,除非它们已移入另一个脚本,然后由重试调用)。

retrycli

重试

pip install git+git://github.com/sky-shiny/retrycli.git
retry my_command