Exécuter une fonction shell avec timeout
pourquoi cela fonctionnerait
timeout 10s echo "foo bar" # foo bar
mais ce ne serait pas
function echoFooBar {
echo "foo bar"
}
echoFooBar # foo bar
timeout 10s echoFooBar # timeout: failed to run command `echoFooBar': No such file or directory
et comment faire pour que ça marche?
7 réponses
timeout
est une commande - donc elle est exécutée dans un sous-processus de votre shell bash. Elle n'a donc aucun accès à vos fonctions définies dans votre shell courant.
la commande timeout
est exécutée comme un sous - processus de timeout-un processus grand-enfant de votre shell.
vous pourriez être confus parce que echo
est à la fois un shell intégré et une commande séparée.
Ce que vous pouvez faire est de mettre votre fonction dans son propre fichier de script, chmod doit être exécutable, puis l'exécuter avec timeout
.
alternativement bifurquer, exécuter votre fonction dans un sous - shell-et dans le processus original, surveiller la progression, tuer le sous-processus si cela prend trop de temps.
comme L'a dit Douglas Leeder, vous avez besoin d'un processus séparé pour signaler un temps mort. Contournez le problème en exportant la fonction vers les sous-cellules et en lançant subshell manuellement.
export -f echoFooBar
timeout 10s bash -c echoFooBar
il y a une alternative en ligne qui lance aussi un sous-processus de bash shell:
timeout 10s bash <<EOT
function echoFooBar {
echo foo
}
echoFooBar
sleep 20
EOT
, Vous pouvez créer une fonction qui vous permettra de faire la même chose que timeout, mais aussi pour d'autres fonctions:
function run_cmd {
cmd=""; timeout="";
grep -qP '^\d+$' <<< $timeout || timeout=10
(
eval "$cmd" &
child=$!
trap -- "" SIGTERM
(
sleep $timeout
kill $child 2> /dev/null
) &
wait $child
)
}
et pourrait s'exécuter comme ci-dessous:
run_cmd "echoFooBar" 10
Note: la solution est venue d'une de mes questions: solution élégante pour implémenter le temps mort pour les commandes et les fonctions de bash
si vous voulez juste ajouter timeout comme une option supplémentaire pour l'ensemble du script existant, vous pouvez le faire tester pour l'option timeout -, et ensuite le faire l'appeler soi-même de façon récursive sans cette option.
example.sh:
#!/bin/bash
if [ "" == "-t" ]; then
timeout 1m "151900920"
else
#the original script
echo
sleep 2m
echo YAWN...
fi
exécuter ce script sans timeout:
$./example.sh -other_option # -other_option
# YAWN...
en cours d'exécution avec une minute de délai d'attente:
$./example.sh -t -other_option # -other_option
function foo(){
for i in {1..100};
do
echo $i;
sleep 1;
done;
}
cat <( foo ) # Will work
timeout 3 cat <( foo ) # Will Work
timeout 3 cat <( foo ) | sort # Wont work, As sort will fail
cat <( timeout 3 cat <( foo ) ) | sort -r # Will Work
cette fonction n'utilise que les builtins
-
peut - être envisager d'évaluer "$ * "au lieu de lancer $@ directement selon vos besoins
-
il démarre un travail avec la chaîne de commande spécifiée après le premier arg qui est la valeur de timeout et surveille le pid de travail
-
il vérifie toutes les 1 secondes, bash prend en charge les temps morts jusqu'à 0,01 de sorte que peut être modifié
-
aussi si votre script a besoin de stdin,
read
doit s'appuyer sur un fd dédié (exec {tofd}<> <(:)
) -
vous pourriez aussi vouloir modifier le signal de kill (celui à l'intérieur de la boucle) qui est par défaut à
-15
, vous pourriez vouloir-9
## forking is evil
timeout() {
to=; shift
$@ & local wp=$! start=0
while kill -0 $wp; do
read -t 1
start=$((start+1))
if [ $start -ge $to ]; then
kill $wp && break
fi
done
}