Search code examples
bashcygwin

How do I limit the running time of a BASH script


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

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.

For example:

$ limittime -t 30 'myscript.sh'

or

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

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


Solution

  • See the http://www.pixelbeat.org/scripts/timeout script the functionality of which has been integrated into newer 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 $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