Another self-explanatory bash hack. This one was developed to limit the total run time of a test script, where one of the commands was hanging but I was trying to chase down a different bug.

# Run code with a time limit.  This is trickier than you'd think, because the alarm
# signal (or any other) won't be delivered to the parent until any foreground task
# completes.  That kind of defeats the purpose here, since a hung task will also
# block the signal we're using to un-hang it.  Fortunately, a directed "wait" gives
# us a way to work around this issue.  We start both the alarm task and the task
# that does real work in the background, then either way we get into exit_handler
# and kill whichever one's still running.  It's a little bit inconvenient that
# everything has to be wrapped in a "main" function to work, but there's a lot about
# bash that's unfortunate.
function exit_handler {
	if [ -n "$ALREADY_EXITING" ]; then
	if [ -n "$WATCHER" ]; then
		echo "killing watcher"
	if [ -n "$WORKER" ]; then
		echo "killing worker"
	echo "time to die"
trap exit_handler EXIT
function alrm_handler {
	echo "alarm went off"
	unset WATCHER
trap alrm_handler ALRM
export PARENT_PID=$$
(sleep $TIME_LIMIT; echo "ring ring"; kill -s SIGALRM $PARENT_PID) &
export WATCHER=$!
# Example function to demonstrate different completion sequences.
function main {
	if [ "$1" != 0 ]; then
		echo "sleeping"
		sleep $1
		echo "waking up"
main "$@" &
export WORKER=$!
wait $WORKER
unset WORKER
# Test with shorter sleep times to see the worker finish normally, with longer
# sleep times to see the watcher cut things short.