2012-03-31 10 views
31

Perché questo lavoroeseguire la funzione con timeout

timeout 10s echo "foo bar" # foo bar 

ma questo non

function echoFooBar { 
    echo "foo bar" 
} 

echoFooBar # foo bar 

timeout 10s echoFooBar # timeout: failed to run command `echoFooBar': No such file or directory 

e sarebbe come posso farlo funzionare?

+0

http://stackoverflow.com/questions/12321469/retry-a-bash-command-with-timeout/35977896#35977896 –

risposta

32

timeout è un comando, quindi è in esecuzione in un sottoprocesso della shell di bash. Pertanto non ha accesso alle tue funzioni definite nella tua shell corrente.

Il comando timeout viene eseguito come sottoprocesso di timeout: un processo grand-child della shell.

Potresti essere confuso perché echo è sia un built-in della shell che un comando separato.

Quello che puoi fare è mettere la tua funzione nel proprio file di script, chmod per essere eseguibile, quindi eseguirla con timeout.

In alternativa fork, esecuzione della funzione in una sotto-shell - e nel processo originale, monitorare l'avanzamento, uccidere il sottoprocesso se richiede troppo tempo.

+0

grazie per la vostra soluzione! Ma poiché voglio aggiungere il timeout come opzione aggiuntiva per uno script esistente, sarebbe piuttosto scomodo avere un proprio file solo per la funzionalità di timeout. È questa l'unica soluzione? – speendo

+4

@speendo Considera che 'timeout' funziona uccidendo i processi inviando loro dei segnali - questo è qualcosa che puoi fare solo per i processi. Quindi qualunque cosa tu corri con il timeout deve essere il suo processo. –

+1

@speendo Si noti inoltre che bash è (AFAIK) a thread singolo, quindi cosa può fare la funzionalità di timeout se il thread sta eseguendo la propria funzione? –

3

se si desidera aggiungere il timeout come opzione aggiuntiva per l'intero script esistente, è possibile farlo testare per l'opzione di timeout, quindi fare in modo che lo chiamino in modo ricorsivo senza quell'opzione.

example.sh:

#!/bin/bash 
if [ "$1" == "-t" ]; then 
    timeout 1m $0 $2 
else 
    #the original script 
    echo $1 
    sleep 2m 
    echo YAWN... 
fi 

l'esecuzione di questo script senza timeout:

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

in esecuzione con un timeout di un minuto:

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

C'è un'alternativa in linea anche il lancio di un sottoprocesso di shell bash:

 

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

echoFooBar 
sleep 20 
EOT 
 
8

È possibile creare una funzione che ti permettono di fare lo stesso come timeout, ma anche per altre funzioni:

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 
    ) 
} 

E potrebbe funzionare come di seguito:

run_cmd "echoFooBar" 10 

Nota: La soluzione venuto da una delle mie domande: Elegant solution to implement timeout for bash commands and functions

+0

non si dovrebbe uccidere anche la subshell più interna dopo "wait $ child'? non fa nulla di dannoso (a parte aspettare), ma continua a contare, anche se il bambino ha finito – Blauhirn

2
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 
19

Come ha affermato Douglas Leeder, è necessario un processo separato per il timeout da segnalare. Soluzione alternativa esportando la funzione in subshell e eseguendo manualmente subshell.

export -f echoFooBar 
timeout 10s bash -c echoFooBar 
+0

Esporta -f non funziona in sh, fa in bash. – J0hnG4lt