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 puis-je le faire fonctionner?
timeout
est une commande - elle est donc exécutée dans un sous-processus de votre shell bash. Par conséquent, il n’a pas accès à vos fonctions définies dans votre shell actuel.
La commande timeout
est donnée est exécutée en tant que sous-processus de timeout - un processus grand-enfant de votre shell.
Vous pourriez être dérouté parce que echo
est à la fois une commande intégrée au shell et une commande distincte.
Ce que vous pouvez faire est de placer votre fonction dans son propre fichier script, chmod pour qu’elle soit exécutable, puis exécutez-la avec timeout
.
Vous pouvez également créer un fork, qui exécute votre fonction dans un sous-shell - et dans le processus d'origine, surveille la progression, en supprimant le sous-processus s'il est trop long.
Comme le disait Douglas Leeder, vous avez besoin d'un processus distinct pour le délai d'attente. Solution de contournement en exportant la fonction vers des sous-réservoirs et en exécutant le sous-shell manuellement.
export -f echoFooBar
timeout 10s bash -c echoFooBar
Une alternative en ligne lance également 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 permettrait de faire la même chose que timeout mais aussi pour d’autres fonctions:
function run_cmd {
cmd="$1"; timeout="$2";
grep -qP '^\d+$' <<< $timeout || timeout=10
(
eval "$cmd" &
child=$!
trap -- "" SIGTERM
(
sleep $timeout
kill $child 2> /dev/null
) &
wait $child
)
}
Et pourrait fonctionner comme ci-dessous:
run_cmd "echoFooBar" 10
Note: La solution est venue d’une de mes questions: Solution élégante pour implémenter le timeout pour les commandes et fonctions bash
si vous souhaitez simplement ajouter timeout en tant qu'option supplémentaire pour l'ensemble du script existant, vous pouvez le tester pour l'option timeout, puis le faire appeler lui-même récursivement sans cette option.
exemple.sh:
#!/bin/bash
if [ "$1" == "-t" ]; then
timeout 1m $0 $2
else
#the original script
echo $1
sleep 2m
echo YAWN...
fi
exécuter ce script sans délai d'expiration:
$./example.sh -other_option # -other_option
# YAWN...
l'exécuter avec un délai d'une minute:
$./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 utilise uniquement les fonctions intégrées
Peut-être envisager d'évaluer "$ *" au lieu d'exécuter directement $ @ en fonction de vos besoins
Il démarre un travail avec la chaîne de commande spécifiée après le premier argument qui correspond à la valeur de délai d'attente et surveille le pid du travail.
Il vérifie toutes les 1 secondes, bash prend en charge les délais d’exécution jusqu’à 0.01, ce qui permet de le modifier.
De plus, si votre script nécessite stdin, read
doit s'appuyer sur un fd dédié (exec {tofd}<> <(:)
)
Aussi, vous voudrez peut-être modifier le signal d’arrêt (celui à l’intérieur de la boucle) qui est la valeur par défaut de -15
, vous pourriez vouloir -9
## forking is evil
timeout() {
to=$1; 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
}
En résumé, ma réponse à la réponse de Tiago Lopo est plus lisible:
Je pense qu'il est plus lisible d'imposer un délai d'expiration au sous-shell le plus récent. De cette manière, nous n'avons pas besoin d'évaluer une chaîne et le script entier peut être mis en surbrillance comme Shell par votre éditeur favori. Je mets simplement les commandes après que le sous-shell avec eval
se soit créé dans une fonction Shell (testé avec zsh, mais devrait fonctionner avec bash):
timeout_child () {
trap -- "" SIGTERM
child=$!
timeout=$1
(
sleep $timeout
kill $child
) &
wait $child
}
Exemple d'utilisation:
( while true; do echo -n .; sleep 0.1; done) & timeout_child 2
Et de cette façon, cela fonctionne aussi avec une fonction Shell (si elle tourne en arrière-plan):
print_dots () {
while true
do
sleep 0.1
echo -n .
done
}
> print_dots & timeout_child 2
[1] 21725
[3] 21727
...................[1] 21725 terminated print_dots
[3] + 21727 done ( sleep $timeout; kill $child; )