如何限制 BASH 脚本的运行时间

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/526782/
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-09 17:56:12  来源:igfitidea点击:

How do I limit the running time of a BASH script

bashcygwin

提问by jm.

I have a long running BASH script that I am running under CYGWIN on Windows.

我有一个长时间运行的 BASH 脚本,我在 Windows 上的 CYGWIN 下运行。

I would like to limit the script to run for 30 seconds, and automatically terminate if it exceeds this limit. Ideally, I'd like to be able to do this to any command.

我想限制脚本运行 30 秒,并在超过此限制时自动终止。理想情况下,我希望能够对任何命令执行此操作。

For example:

例如:

sh-3.2$ limittime -t 30 'myscript.sh'

or

或者

sh-3.2$ limittime -t 30 'grep func *.c'

Under cygwin the ulimit command doesn't seem to work.

在 cygwin 下, ulimit 命令似乎不起作用。

I am open to any ideas.

我愿意接受任何想法。

采纳答案by pixelbeat

See the http://www.pixelbeat.org/scripts/timeoutscript the functionality of which has been integrated into newer coreutils:

请参阅http://www.pixelbeat.org/scripts/timeout脚本,其功能已集成到较新的 coreutils 中:

#!/bin/sh

# Execute a command with a timeout

# License: LGPLv2
# Author:
#    http://www.pixelbeat.org/
# Notes:
#    Note there is a timeout command packaged with coreutils since v7.0
#    If the timeout occurs the exit status is 124.
#    There is an asynchronous (and buggy) equivalent of this
#    script packaged with bash (under /usr/share/doc/ in my distro),
#    which I only noticed after writing this.
#    I noticed later again that there is a C equivalent of this packaged
#    with satan by Wietse Venema, and copied to forensics by Dan Farmer.
# Changes:
#    V1.0, Nov  3 2006, Initial release
#    V1.1, Nov 20 2007, Brad Greenlee <[email protected]>
#                       Make more portable by using the 'CHLD'
#                       signal spec rather than 17.
#    V1.3, Oct 29 2009, Ján Sáreník <[email protected]>
#                       Even though this runs under dash,ksh etc.
#                       it doesn't actually timeout. So enforce bash for now.
#                       Also change exit on timeout from 128 to 124
#                       to match coreutils.
#    V2.0, Oct 30 2009, Ján Sáreník <[email protected]>
#                       Rewritten to cover compatibility with other
#                       Bourne shell implementations (pdksh, dash)

if [ "$#" -lt "2" ]; then
    echo "Usage:   `basename 
#!/usr/bin/bash

sleep 60 &
pid=$!
sleep 10
kill -9 $pid

sleep 3 &
pid=$!
sleep 10
kill -9 $pid
` timeout_in_seconds command" >&2 echo "Example: `basename
$ ./limit10
./limit10: line 9:  4492 Killed sleep 60
./limit10: line 11: kill: (4560) - No such process
` 2 sleep 3 || echo timeout" >&2 exit 1 fi cleanup() { trap - ALRM #reset handler to default kill -ALRM $a 2>/dev/null #stop timer subshell if running kill $! 2>/dev/null && #kill last job exit 124 #exit with 124 if it was running } watchit() { trap "cleanup" ALRM sleep & wait kill -ALRM $$ } watchit & a=$! #start the timeout shift #first param was timeout for sleep trap "cleanup" ALRM INT #cleanup after timeout "$@"& wait $!; RET=$? #start the job wait for it and save its return value kill -ALRM $a #send ALRM signal to watchit wait $a #wait for watchit to finish cleanup exit $RET #return the value

回答by paxdiablo

The following script shows how to do this using background tasks. The first section kills a 60-second process after the 10-second limit. The second attempts to kill a process that's already exited. Keep in mind that, if you set your timeout really high, the process IDs may roll over and you'll kill the wrong process but this is more of a theoretical issue - the timeout would have to be verylarge and you would have to be starting a lotof processes.

以下脚本显示了如何使用后台任务执行此操作。第一部分在 10 秒限制之后终止了 60 秒的进程。第二次尝试杀死已经退出的进程。请记住,如果您将超时设置得非常高,则进程 ID 可能会翻转并且您将终止错误的进程,但这更多是一个理论问题 - 超时必须非常大,您必须启动很多进程。

#!/usr/bin/bash

date
sleep 3 &
pid=$!
((lim = 10))
while [[ $lim -gt 0 ]] ; do
    sleep 1
    proc=$(ps -ef | awk -v pid=$pid '==pid{print}{}')
    echo $proc
    ((lim = lim - 1))
    if [[ -z "$proc" ]] ; then
            ((lim = -9))
    fi
done
date
if [[ $lim -gt -9 ]] ; then
    kill -9 $pid
fi
date

Here's the output on my Cygwin box:

这是我的 Cygwin 盒子上的输出:

Mon Feb  9 11:10:37 WADT 2009
pax 4268 2476 con 11:10:37 /usr/bin/sleep
pax 4268 2476 con 11:10:37 /usr/bin/sleep
Mon Feb  9 11:10:41 WADT 2009
Mon Feb  9 11:10:41 WADT 2009

If you want to only wait until the process has finished, you need to enter a loop and check. This is slightly less accurate since sleep 1and the other commands will actually take more than one second (but not much more). Use this script to replace the second section above (the "echo $proc" and "date" commands are for debugging, I wouldn't expect to have them in the final solution).

如果只想等到进程完成,则需要进入循环并检查。这稍微不太准确,因为sleep 1其他命令实际上需要一秒钟以上(但不会更多)。使用此脚本替换上面的第二部分(“ echo $proc”和“ date”命令用于调试,我不希望在最终解决方案中包含它们)。

Mon Feb  9 11:11:51 WADT 2009
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
pax 4176 2600 con 11:11:51 /usr/bin/sleep
Mon Feb  9 11:12:03 WADT 2009
Mon Feb  9 11:12:03 WADT 2009
./limit10: line 20:  4176 Killed sleep 60

It basically loops, checking if the process is still running every second. If not, it exits the loop with a special value to not try and kill the child. Otherwise it times out and does kill the child.

它基本上是循环的,每秒检查进程是否仍在运行。如果没有,它会以一个特殊值退出循环,以免试图杀死孩子。否则它会超时并杀死孩子。

Here's the output for a sleep 3:

这是 a 的输出sleep 3

timeout 30s YOUR_COMMAND COMMAND_ARGUMENTS

and a sleep 60:

和一个sleep 60

$ timeout --help
Usage: timeout [OPTION] DURATION COMMAND [ARG]...
  or:  timeout [OPTION]
Start COMMAND, and kill it if still running after DURATION.

Mandatory arguments to long options are mandatory for short options too.
      --preserve-status
                 exit with the same status as COMMAND, even when the
                   command times out
      --foreground
                 when not running timeout directly from a shell prompt,
                   allow COMMAND to read from the TTY and get TTY signals;
                   in this mode, children of COMMAND will not be timed out
  -k, --kill-after=DURATION
                 also send a KILL signal if COMMAND is still running
                   this long after the initial signal was sent
  -s, --signal=SIGNAL
                 specify the signal to be sent on timeout;
                   SIGNAL may be a name like 'HUP' or a number;
                   see 'kill -l' for a list of signals
      --help     display this help and exit
      --version  output version information and exit

DURATION is a floating point number with an optional suffix:
's' for seconds (the default), 'm' for minutes, 'h' for hours or 'd' for days.

If the command times out, and --preserve-status is not set, then exit with
status 124.  Otherwise, exit with the status of COMMAND.  If no signal
is specified, send the TERM signal upon timeout.  The TERM signal kills
any process that does not block or catch that signal.  It may be necessary
to use the KILL (9) signal, since this signal cannot be caught, in which
case the exit status is 128+9 rather than 124.

GNU coreutils online help: <http://www.gnu.org/software/coreutils/>
Full documentation at: <http://www.gnu.org/software/coreutils/timeout>
or available locally via: info '(coreutils) timeout invocation'

回答by Chris Bunch

Check out this link. The idea is just that you would run myscript.shas a subprocess of your script and record its PID, then kill it if it runs too long.

查看此链接。这个想法只是您将myscript.sh作为脚本的子进程运行并记录其 PID,然后在运行时间过长时将其杀死。

回答by Jason Cohen

You could run the command as a background job (i.e. with "&"), use the bash variable for "pid of last command run," sleep for the requisite amount of time, then run killwith that pid.

您可以将命令作为后台作业运行(即使用“&”),将 bash 变量用于“上次命令运行的 pid”,休眠所需的时间,然后kill使用该 pid运行。

回答by VeraKozya

##代码##

Below are all the options for "timeout" under coreutils:

以下是 coreutils 下“超时”的所有选项:

##代码##