Esegue una funzione di shell con timeout

Perché dovrebbe funzionare?

timeout 10s echo "foo bar" # foo bar 

ma questo non lo farebbe

 function echoFooBar { echo "foo bar" } echoFooBar # foo bar timeout 10s echoFooBar # timeout: failed to run command `echoFooBar': No such file or directory 

e come posso farlo funzionare?

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

Il timeout comando è dato 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 suo file di script, chmod per essere eseguibile, quindi eseguirla con timeout .

In alternativa fork, eseguendo la tua funzione in una sub-shell e nel processo originale, controlla l’avanzamento, uccidendo il sottoprocesso se impiega troppo tempo.

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 

Esiste anche un’alternativa in linea che avvia un sottoprocesso di shell bash:

timeout 10s bash <
timeout 10s bash < 

Puoi creare una funzione che ti consenta 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 proveniva da una delle mie domande: soluzione elegante per implementare il timeout per comandi e funzioni bash

se si desidera aggiungere il timeout come opzione aggiuntiva per l’intero script esistente, è ansible farlo testare per l’opzione di timeout, quindi eseguire la chiamata 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 

eseguire questo script senza timeout:

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

eseguendolo con un timeout di un minuto:

 $./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 

Questa funzione utilizza solo i builtin

  • Forse considera di valutare “$ *” invece di eseguire $ @ direttamente in base alle tue esigenze

  • Avvia un lavoro con la stringa di comando specificata dopo il primo argomento che rappresenta il valore di timeout e monitora il processo pid

  • Controlla ogni 1 secondi, bash supporta i timeout fino a 0,01 in modo che possano essere ottimizzati

  • Anche se il tuo script ha bisogno di stdin, read dovrebbe fare affidamento su un fd dedicato ( exec {tofd}<> <(:) )

  • Potresti anche voler modificare il segnale kill (quello all'interno del loop) che è predefinito a -15 , potresti volere -9

 ## forking is evil timeout() { to=$1; shift [email protected] & 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 }