Ho uno script di shell si tratta di leggere dallo standard input . In rare circostanze, non ci sarà nessuno pronto a fornire input e lo script deve scadere . In caso di timeout, lo script deve eseguire del codice di pulizia. Qual è il modo migliore per farlo?
Questo script deve essere molto portabile , inclusi i sistemi Unix del 20° secolo senza un compilatore C e i dispositivi embedded che eseguono busybox, quindi non è possibile fare affidamento su Perl, bash, qualsiasi linguaggio compilato e persino l'intero POSIX.2. In particolare, $PPID
, read -t
e non sono disponibili trappole perfettamente conformi a POSIX. È esclusa anche la scrittura su un file temporaneo; lo script potrebbe essere eseguito anche se tutti i filesystem sono montati in sola lettura.
Per rendere le cose più difficili, voglio anche che la sceneggiatura sia ragionevolmente veloce quando non è scaduto. In particolare, utilizzo lo script anche in Windows (principalmente in Cygwin), dove fork ed exec sono particolarmente bassi, quindi voglio mantenerne l'utilizzo al minimo.
In poche parole, ho
trap cleanup 1 2 3 15
foo=`cat`
e voglio aggiungere un timeout. Non posso sostituire cat
con il read
incorporato. In caso di timeout, voglio eseguire la cleanup
funzione.
Sfondo:questo script sta indovinando la codifica del terminale stampando alcuni caratteri a 8 bit e confrontando la posizione del cursore prima e dopo. L'inizio dello script verifica che stdout sia connesso a un terminale supportato, ma a volte l'ambiente sta mentendo (ad es. plink
imposta TERM=xterm
anche se viene chiamato con TERM=dumb
). La parte rilevante dello script si presenta così:
text='Éé' # UTF-8; shows up as Ãé on a latin1 terminal
csi='␛['; dsr_cpr="${csi}6n"; dsr_ok="${csi}5n" # ␛ is an escape character
stty_save=`stty -g`
cleanup () { stty "$stty_save"; }
trap 'cleanup; exit 120' 0 1 2 3 15 # cleanup code
stty eol 0 eof n -echo # Input will end with `0n`
# echo-n is a function that outputs its argument without a newline
echo-n "$dsr_cpr$dsr_ok" # Ask the terminal to report the cursor position
initial_report=`tr -dc ;0123456789` # Expect ␛[42;10R␛[0n for y=42,x=10
echo-n "$text$dsr_cpr$dsr_ok"
final_report=`tr -dc ;0123456789`
cleanup
# Compute and return initial_x - final_x
Come posso modificare lo script in modo che se tr
non ha letto alcun input dopo 2 secondi, viene terminato e lo script esegue la cleanup
funzione?
Risposta accettata:
Che ne dici di questo:
foo=`{ { cat 1>&3; kill 0; } | { sleep 2; kill 0; } } 3>&1`
Cioè:esegui il comando di produzione dell'output e sleep
nello stesso gruppo di processi, un gruppo di processi solo per loro. Qualunque comando venga restituito per primo, uccide l'intero gruppo di processi.
Qualcuno si chiederebbe:Sì, il tubo non viene utilizzato; viene bypassato utilizzando i reindirizzamenti. L'unico scopo è che la shell esegua i due processi nello stesso gruppo di processi.
Correlati:uno script per ridurre e comprimere le immagini del disco DMG non sembra funzionare bene nello scenario?Come ha sottolineato Gilles nel suo commento, questo non funzionerà in uno script di shell perché il processo di script verrebbe interrotto insieme ai due sottoprocessi.
Un modo¹ per forzare l'esecuzione di un comando in un gruppo di processi separato consiste nell'avviare una nuova shell interattiva:
#!/bin/sh
foo=`sh -ic '{ cat 1>&3; kill 0; } | { sleep 2; kill 0; }' 3>&1 2>/dev/null`
[ -n "$foo" ] && echo got: "$foo" || echo timeouted
Ma potrebbero esserci degli avvertimenti con questo (ad esempio quando stdin non è un tty?). Il reindirizzamento stderr serve per eliminare il messaggio "Terminated" quando la shell interattiva viene terminata.
Testato con zsh
,bash
e dash
. Ma che dire dei vecchi?
B98 suggerisce la seguente modifica, lavorando su Mac OS X, con GNU bash 3.2.57, o Linux con dash:
foo=`sh -ic 'exec 3>&1 2>/dev/null; { cat 1>&3; kill 0; } | { sleep 2; kill 0; }'`
–