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?

37
demandé sur codeforester 2012-03-31 13:40:21

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.

37
répondu Douglas Leeder 2012-03-31 16:17:35

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
25
répondu user3132194 2015-07-09 08:11:11

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

18
répondu Eduardo Lago Aguilar 2013-04-24 20:26:14

, 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

8
répondu Tiago Lopo 2017-05-23 12:26:36

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
3
répondu Superole 2013-03-06 10:59:19
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 
2
répondu Hemant Patel 2015-07-08 06:46:04

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
}
1
répondu untore 2018-05-29 14:10:30