J'ai une légère modification de la réponse de @Tiago Lopo qui peut gérer les commandes avec plusieurs arguments. J'ai également testé la solution de TauPan, mais elle ne fonctionne pas si vous l'utilisez plusieurs fois dans un script, alors que celle de Tiago le fait.
function timeout_cmd {
local arr
local cmd
local timeout
arr=( "$@" )
# timeout: first arg
# cmd: the other args
timeout="${arr[0]}"
cmd=( "${arr[@]:1}" )
(
eval "${cmd[@]}" &
child=$!
echo "child: $child"
trap -- "" SIGTERM
(
sleep "$timeout"
kill "$child" 2> /dev/null
) &
wait "$child"
)
}
Voici un script entièrement fonctionnel que vous pouvez utiliser pour tester la fonction ci-dessus :
$ ./test_timeout.sh -h
Usage:
test_timeout.sh [-n] [-r REPEAT] [-s SLEEP_TIME] [-t TIMEOUT]
test_timeout.sh -h
Test timeout_cmd function.
Options:
-n Dry run, do not actually sleep.
-r REPEAT Reapeat everything multiple times [default: 1].
-s SLEEP_TIME Sleep for SLEEP_TIME seconds [default: 5].
-t TIMEOUT Timeout after TIMEOUT seconds [default: no timeout].
Par exemple, vous pouvez lancer le programme comme ceci :
$ ./test_timeout.sh -r 2 -s 5 -t 3
Try no: 1
- Set timeout to: 3
child: 2540
-> retval: 143
-> The command timed out
Try no: 2
- Set timeout to: 3
child: 2593
-> retval: 143
-> The command timed out
Done!
#!/usr/bin/env bash
#shellcheck disable=SC2128
SOURCED=false && [ "$0" = "$BASH_SOURCE" ] || SOURCED=true
if ! $SOURCED; then
set -euo pipefail
IFS=$'\n\t'
fi
#################### helpers
function check_posint() {
local re='^[0-9]+$'
local mynum="$1"
local option="$2"
if ! [[ "$mynum" =~ $re ]] ; then
(echo -n "Error in option '$option': " >&2)
(echo "must be a positive integer, got $mynum." >&2)
exit 1
fi
if ! [ "$mynum" -gt 0 ] ; then
(echo "Error in option '$option': must be positive, got $mynum." >&2)
exit 1
fi
}
#################### end: helpers
#################### usage
function short_usage() {
(>&2 echo \
"Usage:
test_timeout.sh [-n] [-r REPEAT] [-s SLEEP_TIME] [-t TIMEOUT]
test_timeout.sh -h"
)
}
function usage() {
(>&2 short_usage )
(>&2 echo \
"
Test timeout_cmd function.
Options:
-n Dry run, do not actually sleep.
-r REPEAT Reapeat everything multiple times [default: 1].
-s SLEEP_TIME Sleep for SLEEP_TIME seconds [default: 5].
-t TIMEOUT Timeout after TIMEOUT seconds [default: no timeout].
")
}
#################### end: usage
help_flag=false
dryrun_flag=false
SLEEP_TIME=5
TIMEOUT=-1
REPEAT=1
while getopts ":hnr:s:t:" opt; do
case $opt in
h)
help_flag=true
;;
n)
dryrun_flag=true
;;
r)
check_posint "$OPTARG" '-r'
REPEAT="$OPTARG"
;;
s)
check_posint "$OPTARG" '-s'
SLEEP_TIME="$OPTARG"
;;
t)
check_posint "$OPTARG" '-t'
TIMEOUT="$OPTARG"
;;
\?)
(>&2 echo "Error. Invalid option: -$OPTARG.")
(>&2 echo "Try -h to get help")
short_usage
exit 1
;;
:)
(>&2 echo "Error.Option -$OPTARG requires an argument.")
(>&2 echo "Try -h to get help")
short_usage
exit 1
;;
esac
done
if $help_flag; then
usage
exit 0
fi
#################### utils
if $dryrun_flag; then
function wrap_run() {
( echo -en "[dry run]\\t" )
( echo "$@" )
}
else
function wrap_run() { "$@"; }
fi
# Execute a shell function with timeout
# https://stackoverflow.com/a/24416732/2377454
function timeout_cmd {
local arr
local cmd
local timeout
arr=( "$@" )
# timeout: first arg
# cmd: the other args
timeout="${arr[0]}"
cmd=( "${arr[@]:1}" )
(
eval "${cmd[@]}" &
child=$!
echo "child: $child"
trap -- "" SIGTERM
(
sleep "$timeout"
kill "$child" 2> /dev/null
) &
wait "$child"
)
}
####################
function sleep_func() {
local secs
local waitsec
waitsec=1
secs=$(($1))
while [ "$secs" -gt 0 ]; do
echo -ne "$secs\033[0K\r"
sleep "$waitsec"
secs=$((secs-waitsec))
done
}
command=("wrap_run" \
"sleep_func" "${SLEEP_TIME}"
)
for i in $(seq 1 "$REPEAT"); do
echo "Try no: $i"
if [ "$TIMEOUT" -gt 0 ]; then
echo " - Set timeout to: $TIMEOUT"
set +e
timeout_cmd "$TIMEOUT" "${command[@]}"
retval="$?"
set -e
echo " -> retval: $retval"
# check if (retval % 128) == SIGTERM (== 15)
if [[ "$((retval % 128))" -eq 15 ]]; then
echo " -> The command timed out"
fi
else
echo " - No timeout"
"${command[@]}"
retval="$?"
fi
done
echo "Done!"
exit 0