I tend to do something like this. Normally, I wouldn't include the extra jobs calls and extra echo calls, these are just to show what is happening.
#!/usr/bin/env bash
runUntilDoneOrTimeout () {
local -i timeout=0
OPTIND=1
while getopts "t:" opt; do
case $opt in
t) timeout=$OPTARG;;
esac
done
shift $((OPTIND - 1))
runCommand="$*"
$runCommand &
runPID=$!
echo checking jobs
jobs # just to prove there are some
echo job check complete
while jobs %- >& /dev/null && ((timeout > 0)); do
echo "waiting for $runCommand for $timeout seconds"
sleep 1
((timeout--))
done
if (( timeout == 0 )); then
echo "$runCommand timed out"
kill -9 $runPID
wait $runPID
else
echo "$runCommand completed"
fi
echo checking jobs
jobs # just to prove there are none
echo job check complete
}
declare -i timeopt=10
declare -i sleepopt=100
OPTIND=1
while getopts "t:s:" opt; do
case $opt in
t) timeopt=$OPTARG;;
s) sleepopt=$OPTARG;;
esac
done
shift $((OPTIND - 1))
runUntilDoneOrTimeout -t $timeopt sleep $sleepopt