#!/bin/ksh # $Header: /afs/northstar/ufac/richard/projects/mcasey/run-with-timeout,v 1.5 2008/10/22 16:51:00 richard Exp $ # # Run a command, but kill it if it has not returned after $timeout seconds # # Richard Brittain, 2008/07/05 # # 1.1: tested on OSX 10.4 with ksh # # Make sure backgrounded processes are not auto-niced. set +o bgnice timeout=$1; shift # signal handler to trap ALRM function timeout_handler { # Other logging possible here. echo "timeout after $timeout s: \"$command $args\": killed" >&2 # Kill the process which didn't complete as expected # If $proc isn't set, we got called too soon. [[ -n "$proc" ]] && kill -s TERM $proc # Set an exit status to be returned to indicate timeout estatus=127 } # Get the command and arguments to be run command=$1; shift args="$@" # Set trap on SIGALRM trap timeout_handler ALRM # Now start the alarm timer # Arrange to interrupt ourselves after some timeout # $pid is PID of this shell pid=$$ ( # Sleep for $timeout seconds. If we haven't been killed, send a SIGALRM to our caller sleep $timeout kill -s ALRM $pid ) & # $alarm is the PID of the alarm subshell itself alarm=$! # Run the command in background and get the PID in $proc so we can kill it if needed # Using 'eval' allows us to process shell redirection and other metacharacters, but breaks # quoting for any argument with embedded spaces. # Don't allow shell metacharacters as actual arguments. This probably won't work if the command is a pipeline ## eval "$command" "$args" \& $command "$@" & proc=$! # Now wait for the command to complete. Generally this is what will be interrupted if it times out wait $proc pstatus=$? # The process exit status is captured in $pstatus, if it completed. # Kill the alarm process, if it is still running (don't bother testing) kill -s TERM $alarm 2>/dev/null # echo "Exiting with status ${estatus:-$pstatus}" >&2 exit ${estatus:-$pstatus}