代码之家  ›  专栏  ›  技术社区  ›  jm.

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

  •  20
  • jm.  · 技术社区  · 16 年前

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

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

    例如:

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

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

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

    我对任何想法都持开放态度。

    5 回复  |  直到 13 年前
        1
  •  17
  •   paxdiablo    11 年前

    请参阅 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 <brad@footle.org>
    #                       Make more portable by using the 'CHLD'
    #                       signal spec rather than 17.
    #    V1.3, Oct 29 2009, Ján Sáreník <jasan@x31.com>
    #                       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 <jasan@x31.com>
    #                       Rewritten to cover compatibility with other
    #                       Bourne shell implementations (pdksh, dash)
    
    if [ "$#" -lt "2" ]; then
        echo "Usage:   `basename $0` timeout_in_seconds command" >&2
        echo "Example: `basename $0` 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 $1& wait
        kill -ALRM $$
    }
    
    watchit $1& 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
    
        2
  •  12
  •   paxdiablo    15 年前

    以下脚本显示了如何使用后台任务完成此操作。第一部分在10秒的限制后终止60秒的进程。第二次尝试终止已退出的进程。请记住,如果你将超时设置得很高,进程ID可能会滚动,你会杀死错误的进程,但这更多的是一个理论问题——超时必须是 非常 大,你必须开始一个 许多 过程。

    #!/usr/bin/bash
    
    sleep 60 &
    pid=$!
    sleep 10
    kill -9 $pid
    
    sleep 3 &
    pid=$!
    sleep 10
    kill -9 $pid
    

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

    $ ./limit10
    ./limit10: line 9:  4492 Killed sleep 60
    ./limit10: line 11: kill: (4560) - No such process
    

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

    #!/usr/bin/bash
    
    date
    sleep 3 &
    pid=$!
    ((lim = 10))
    while [[ $lim -gt 0 ]] ; do
        sleep 1
        proc=$(ps -ef | awk -v pid=$pid '$2==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
    

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

    这是a的输出 sleep 3 :

    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
    

    以及a sleep 60 :

    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
    
        3
  •  5
  •   Chris Bunch    16 年前

    查看 this link 这个想法就是你会跑 myscript.sh 作为脚本的子进程,记录其PID,如果运行时间过长,则将其终止。

        4
  •  2
  •   Jason Cohen    16 年前
    timeout 30s YOUR_COMMAND COMMAND_ARGUMENTS
    

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

    $ 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'
    
        5
  •  1
  •   VeraKozya    5 年前

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