#!/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 # Make more portable by using the 'CHLD' # signal spec rather than 17. # V1.3, Oct 29 2009, Ján Sáreník # 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 # Rewritten to cover compatibility with other # Bourne shell implementations (pdksh, dash) if test "$Argc" -lt "2" { echo "Usage: $[basename $0] timeout_in_seconds command" > !2 echo "Example: $[basename $0] 2 sleep 3 || echo timeout" > !2 exit 1 } proc cleanup { trap - ALRM #reset handler to default kill -ALRM $a !2 >/dev/null #stop timer subshell if running kill $BgPid !2 >/dev/null && #kill last job exit 124 #exit with 124 if it was running } proc watchit { trap "cleanup" ALRM sleep $1& wait kill -ALRM $Pid } watchit $1& global a := $BgPid #start the timeout shift #first param was timeout for sleep trap "cleanup" ALRM INT #cleanup after timeout @Argv& wait $BgPid; global RET := $Status #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 (CommandList children: [ (If arms: [ (if_arm cond: [ (Sentence child: (C {(Lit_Other "[")} {(DQ ($ VSub_Pound "$#"))} {(-lt)} {(DQ (2))} {(Lit_Other "]")}) terminator: ) ] action: [ (SimpleCommand words: [ {(echo)} { (DQ ("Usage: ") (CommandSubPart command_list: (CommandList children:[(C {(basename)} {($ VSub_Number "$0")})]) left_token: spids: [105 109] ) (" timeout_in_seconds command") ) } ] redirects: [(Redir op_id:Redir_GreatAnd fd:-1 arg_word:{(2)} spids:[113])] ) (SimpleCommand words: [ {(echo)} { (DQ ("Example: ") (CommandSubPart command_list: (CommandList children:[(C {(basename)} {($ VSub_Number "$0")})]) left_token: spids: [121 125] ) (" 2 sleep 3 || echo timeout") ) } ] redirects: [(Redir op_id:Redir_GreatAnd fd:-1 arg_word:{(2)} spids:[129])] ) (C {(exit)} {(1)}) ] spids: [-1 98] ) ] spids: [-1 137] ) (FuncDef name: cleanup body: (BraceGroup children: [ (C {(trap)} {(-)} {(ALRM)}) (SimpleCommand words: [{(kill)} {(-ALRM)} {($ VSub_Name "$a")}] redirects: [(Redir op_id:Redir_Great fd:2 arg_word:{(/dev/null)} spids:[163])] ) (AndOr children: [ (SimpleCommand words: [{(kill)} {($ VSub_Bang "$!")}] redirects: [(Redir op_id:Redir_Great fd:2 arg_word:{(/dev/null)} spids:[174])] ) (C {(exit)} {(124)}) ] op_id: Op_DAmp ) ] spids: [144] ) spids: [140 143] ) (FuncDef name: watchit body: (BraceGroup children: [ (C {(trap)} {(DQ (cleanup))} {(ALRM)}) (Sentence child:(C {(sleep)} {($ VSub_Number "$1")}) terminator:) (C {(wait)}) (C {(kill)} {(-ALRM)} {($ VSub_Dollar "$$")}) ] spids: [197] ) spids: [193 196] ) (Sentence child:(C {(watchit)} {($ VSub_Number "$1")}) terminator:) (Assignment keyword: Assign_None pairs: [(assign_pair lhs:(LhsName name:a) op:Equal rhs:{($ VSub_Bang "$!")} spids:[231])] spids: [231] ) (C {(shift)}) (C {(trap)} {(DQ (cleanup))} {(ALRM)} {(INT)}) (Sentence child:(C {(DQ ($ VSub_At "$@"))}) terminator:) (Sentence child:(C {(wait)} {($ VSub_Bang "$!")}) terminator:) (Assignment keyword: Assign_None pairs: [(assign_pair lhs:(LhsName name:RET) op:Equal rhs:{($ VSub_QMark "$?")} spids:[265])] spids: [265] ) (C {(kill)} {(-ALRM)} {($ VSub_Name "$a")}) (C {(wait)} {($ VSub_Name "$a")}) (C {(exit)} {($ VSub_Name "$RET")}) ] )