POSIX equivalente per timeout GNU?


14

Il timeoutcomando GNU coreutils è estremamente utile per alcune situazioni di scripting, consentendo di utilizzare l'output di un comando se è veloce da eseguire e saltarlo se richiederebbe troppo tempo.

Come posso approssimare il comportamento di base timeoutusando solo le utility specificate da POSIX?


(Sto pensando che può comportare una combinazione di wait, sleep, kille chissà che altro, ma forse mi manca un approccio più facile.)


3
Vedi Timeout in uno script di shell , ma non lo considero un duplicato perché ho richiesto la portabilità ai sistemi pre-POSIX e avevo il requisito di preservare lo stdin e lo stdout che alcune soluzioni che coinvolgono i processi in background escludono.
Gilles 'SO- smetti di essere malvagio' il

command & pid=$! ; sleep 5 && kill $pid
Pandya,

1
@Pandya non introduce una leggera condizione di gara, in cui se commandfinisce rapidamente c'è una sottile possibilità di pidessere riutilizzato da un altro processo che si avvia prima dell'esecuzione del killcomando? Non lo vorrei nel codice di produzione ....
Wildcard,

Puoi spiegare di più sul problema di fondo che stai cercando di risolvere? Perché non solo compilare il timeoutprogramma e usarlo?
James Youngman,

2
@JamesYoungman, suppongo che tu non abbia molta familiarità con la scrittura di script per la portabilità . Chiedere un modo di fare qualcosa conforme a POSIX (o specificato da POSIX) implica che si intende che sia portatile. Compilazione del codice sorgente in un binario non è decisamente portatile e, a seconda delle politiche di sicurezza aziendali, non si può avere un compilatore installato a tutti su un server di produzione.
Carattere jolly

Risposte:


2

Il mio approccio sarebbe questo:

  • Esegui comando come processo in background 1

  • Eseguire "timer watchdog" come processo in background 2

  • Configurare un gestore per intercettare un segnale di terminazione nella shell padre

  • Attendere il completamento di entrambi i processi. Il processo che termina per primo, invia il segnale di terminazione al genitore.

  • Il gestore trap del genitore uccide entrambi i processi in background tramite il controllo del lavoro (uno di questi è già terminato per definizione, ma quell'uccisione sarà una non-azione innocua perché non stiamo usando PID, vedi sotto)

Ho cercato di aggirare la possibile condizione di competizione affrontata nei commenti usando gli ID di controllo del lavoro della shell (che non sarebbero ambigui all'interno di questa istanza della shell) per identificare i processi in background da terminare, anziché i PID di sistema.

#!/bin/sh

TIMEOUT=$1
COMMAND='sleep 5'

function cleanup {
    echo "SIGTERM trap"
    kill %1 %2
}

trap cleanup SIGTERM

($COMMAND; echo "Command completed"; kill $$) &
(sleep $TIMEOUT; echo "Timeout expired"; kill $$) &

wait
echo "End of execution"

Risultato per TIMEOUT=10(comando termina prima del watchdog):

$ ./timeout.sh 10
Command completed
SIGTERM trap
End of execution

Risultato per TIMEOUT=1(il watchdog termina prima del comando):

$ ./timeout.sh 1
Timeout expired
SIGTERM trap
End of execution

Risultato per TIMEOUT=5(watchdog e comando terminano "quasi" contemporaneamente):

./tst.sh 5
Timeout expired
Command completed
SIGTERM trap
End of execution

Questo non è conforme a POSIX poiché (a) dovrebbe essere la definizione della funzione cleanup() { ...; }e (b) il controllo del lavoro è una funzione bash non specificata da POSIX (sebbene ksh e altri lo abbiano anche). Bel copione, però!
Wildcard l'

Puoi anche fare di meglio con timeout="$1"; shifte (exec "$@"; echo "Command completed"; kill $$)piuttosto che rielaborare l'elenco di argomenti.
Wildcard l'
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.