83 votes

Exécuter une fonction shell avec timeout

Pourquoi cela fonctionnerait-il

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 ?

73voto

Douglas Leeder Points 29986

timeout est une commande - elle s'exécute donc dans un sous-processus de votre shell bash. Elle n'a donc pas accès aux fonctions définies dans votre shell actuel.

La commande timeout est donné est exécuté comme un sous-processus de timeout - un processus petit-enfant de votre shell.

Vous pouvez être confus parce que echo est à la fois un shell intégré et une commande séparée.

Ce que vous pouvez faire, c'est mettre votre fonction dans son propre fichier script, le modifier pour qu'il soit exécutable, puis l'exécuter avec timeout .

Une autre solution consiste à forker, en exécutant votre fonction dans un sous-shell - et dans le processus original, à surveiller la progression, en tuant le sous-processus si cela prend trop de temps.

71voto

user3132194 Points 46

Comme Douglas Leeder l'a dit, vous avez besoin d'un processus séparé pour le signal de timeout. Vous pouvez contourner ce problème en exportant la fonction vers des sous-shells et en exécutant les sous-shells manuellement.

export -f echoFooBar
timeout 10s bash -c echoFooBar

29voto

Il existe une alternative en ligne qui permet également de lancer un sous-processus du shell bash :

timeout 10s bash <<EOT
function echoFooBar {
  echo foo
}

echoFooBar
sleep 20
EOT

11voto

Tiago Points 2425

Vous pouvez créer une fonction qui vous permettrait de faire la même chose que le 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 issue d'une de mes questions : Solution élégante pour implémenter le timeout pour les commandes et fonctions bash

7voto

Superole Points 401

Si vous voulez simplement ajouter le timeout comme option supplémentaire pour l'ensemble du script existant, vous pouvez le faire tester pour l'option timeout, et ensuite le faire s'appeler 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

en exécutant ce script sans délai d'attente :

$./example.sh -other_option # -other_option
                            # YAWN...

en l'exécutant avec un temps mort d'une minute :

$./example.sh -t -other_option # -other_option

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X