Come non fare nulla per sempre in modo elegante?


82

Ho un programma che produce informazioni utili stdoutma che legge anche stdin. Voglio reindirizzare il suo output standard su un file senza fornire nulla sull'input standard. Fin qui tutto bene: posso fare:

program > output

e non fare nulla nel tty.

Tuttavia, il problema è che voglio farlo in background. Se lo faccio:

program > output &

il programma verrà sospeso ("sospeso (input tty)").

Se lo faccio:

program < /dev/null > output &

il programma termina immediatamente perché raggiunge EOF.

Sembra che ciò di cui ho bisogno sia convogliare programqualcosa che non fa nulla per un tempo indefinito e che non legge stdin. I seguenti approcci funzionano:

while true; do sleep 100; done | program > output &
mkfifo fifo && cat fifo | program > output &
tail -f /dev/null | program > output &

Tuttavia, è tutto molto brutto. Ci deve essere un modo elegante, utilizzando le utility Unix standard, a "non fare nulla, a tempo indeterminato" (parafrasando man true). Come ho potuto raggiungere questo obiettivo? (I miei criteri principali per l'eleganza qui: nessun file temporaneo; nessuna attesa in attesa o riattivazione periodica; nessuna utilità esotica; il più breve possibile.)


Prova su -c 'program | output &' user. Sto per porre una domanda simile con la creazione di lavori in background come metodo accettabile per la gestione di un "servizio / demone". Ho anche notato che non potevo reindirizzare STDERRsenza reindirizzare STDOUT. La soluzione a cui programmaA invia STDOUTa STDINdi programmaB, quindi reindirizza STDERRa un file di registro:programA 2> /var/log/programA.log | programB 2> /var/log/programB.log 1> /dev/null
mbrownnyc

forse ... su -c 'while true; do true; done | cat > ~/output &' user?
Mbrownnyc,

che tipo di programma è quello?
João Portela,

João Portela: Questo è un programma che ho scritto, gitorious.org/irctk
a3nm,

Perché non aggiungere semplicemente un passaggio al programma che hai scritto? Inoltre, suppongo che se chiudi Stdin con 1<&-esso uscirà dal tuo programma?
wt

Risposte:


16

In conchiglie che li (ksh, zsh, bash4) supportano, si può iniziare programcome un co-processo .

  • ksh: program > output |&
  • zsh, bash:coproc program > output

Quello inizia programin background con il suo input reindirizzato da a pipe. L'altra estremità del tubo è aperta al guscio.

Tre vantaggi di questo approccio

  • nessun processo aggiuntivo
  • puoi uscire dallo script quando programmuore (usa waitper aspettarlo)
  • programterminerà (salire eofsul suo stdin se la shell esce).

Sembra funzionare e sembra un'ottima idea! (Per essere onesti, avevo chiesto qualcosa da inviare al mio comando, non per una funzione shell, ma questo era solo il problema XY in gioco.) Sto considerando di accettare questa risposta invece di quella di @ PT.
a3nm,

1
@ a3nm, tail -f /dev/nullnon è l'ideale in quanto esegue una lettura ogni secondo /dev/null(le versioni correnti della coda GNU su Linux usando inotify in realtà c'è un bug ). sleep info il suo equivalente più portatile sleep 2147483647sono approcci migliori per un comando che sta lì a fare nulla IMO (nota che sleepè costruito in alcune shell come ksh93o mksh).
Stéphane Chazelas,

78

Non credo che diventerai più elegante del

tail -f /dev/null

che hai già suggerito (supponendo che questo usi inotificano internamente, non dovrebbero esserci polling o riattivazioni, quindi oltre ad essere strano, dovrebbe essere sufficiente).

È necessaria un'utilità che funzionerà a tempo indeterminato, manterrà il suo stdout aperto, ma in realtà non scriverà nulla su stdout e non uscirà quando il suo stdin è chiuso. Qualcosa del genere in yesrealtà scrive su stdout. catuscirà quando il suo stdin è chiuso (o qualsiasi cosa tu abbia reindirizzato al suo interno). Penso che sleep 1000000000dpotrebbe funzionare, ma tailè chiaramente meglio. La mia casella Debian ha un tailfcomando che accorcia leggermente.

Prendendo una virata diversa, che ne dici di eseguire il programma sotto screen?


Mi piace l' tail -f /dev/nullapproccio migliore e lo trovo anche abbastanza elegante, poiché l'uso del comando corrisponde abbastanza da vicino allo scopo previsto.
jw013,

2
Da strace tail -f /dev/nullciò sembra che gli tailusi inotifye che i risvegli si verifichino in casi sciocchi come sudo touch /dev/null. È triste che non sembra esserci una soluzione migliore ... Mi chiedo quale sarebbe la scala di accesso giusta da usare per implementare una soluzione migliore.
a3nm,

5
@ a3nm Lo syscall sarebbe pause, ma non è esposto direttamente a un'interfaccia shell.
Gilles,

PT: Lo so screen, ma si tratta di eseguire più occorrenze del programma da uno script di shell a scopo di test, quindi l'utilizzo screenè un po 'eccessivo.
a3nm,

3
@sillyMunky Silly Monkey, la risposta di WaelJ è sbagliata (invia zeri infiniti a stdin).
PT

48

sleep infinity è la soluzione più chiara che io conosca.

Puoi usarlo infinityperché sleepaccetta un numero in virgola mobile * , che può essere decimale , esadecimale , infinito o NaN , secondo man strtod.

* Questo non fa parte dello standard POSIX, quindi non è portatile come tail -f /dev/null. Tuttavia, è supportato in GNU coreutils (Linux) e BSD (usato su Mac) (apparentemente non supportato su versioni più recenti di Mac - vedi commenti).


Haha, è davvero un bel approccio. :)
a3nm

@ a3nm: Grazie:) Sembra funzionaresleep infinity anche su BSD e Mac .
Zaz,

Che tipo di risorse prende un processo di sonno infinito? Solo RAM?
CMCDragonkai,

1
Questa risposta afferma che sleep infinityattende al massimo 24 giorni; chi ha ragione?
nh2,

1
@Zaz Ho studiato il problema in dettaglio ora. Si scopre che inizialmente avevi ragione! L' sleeputilità non è limitata a 24 giorni ; è solo il primo syscall che dorme per 24 giorni, e successivamente farà più tali syscall. Vedi il mio commento qui: stackoverflow.com/questions/2935183/…
nh2

19
sleep 2147483647 | program > output &

Sì, 2^31-1è un numero finito e non funzionerà per sempre , ma ti darò $ 1000 quando il sonno finirà finalmente. (Suggerimento: uno di noi sarà morto per allora.)

  • nessun file temporaneo; dai un'occhiata.
  • nessuna sveglia in attesa o periodica; dai un'occhiata
  • nessuna utilità esotica; dai un'occhiata.
  • il più corto possibile. Va bene, potrebbe essere più breve.

5
bash: sleep $ ((64 # 1 _____)) | programma> uscita &
Diego Torres Milano

Che dorme per 68 anni , che dorme per 98 secoli : sleep 2147483647d...
agc,

9

Puoi creare un binario che fa proprio questo con:

$ echo 'int main(){ pause(); }' > pause.c; make pause

5

Ecco un altro suggerimento che utilizza le utility Unix standard, per "non fare nulla, a tempo indeterminato" .

sh -c 'kill -STOP $$' | program > output

Questo attiva una shell che viene immediatamente inviata SIGSTOP, che sospende il processo. Questo è usato come "input" per il tuo programma. Il complemento di SIGSTOPè SIGCONT, ovvero se sai che la shell ha PID 12345 puoi kill -CONT 12345farlo continuare.


2

Su Linux, puoi fare:

read x < /dev/fd/1 | program > output

Su Linux, aprendo / dev / fd / x dove x è un descrittore di file all'estremità di scrittura di una pipe, ottieni la fine di lettura della pipe, quindi qui lo stesso dello stdin del programma. Quindi, fondamentalmente, readnon tornerà mai più, perché l'unica cosa che può scrivere su quella pipe è se stessa e readnon produce nulla.

Funzionerà anche su FreeBSD o Solaris, ma per un altro motivo. Lì, aprendo / dev / fd / 1 ottieni la stessa risorsa di open su fd 1 come ti aspetteresti e come la maggior parte dei sistemi tranne Linux, quindi la fine della scrittura della pipe. Tuttavia, su FreeBSD e Solaris, i tubi sono bidirezionali. Quindi fino a quando programnon scrive sul suo stdin (nessuna applicazione lo fa), readnon otterrà nulla da leggere da quella direzione della pipe.

Sui sistemi in cui le pipe non sono bidirezionali, readprobabilmente falliranno con un errore quando si tenta di leggere da un descrittore di file di sola scrittura. Si noti inoltre che non tutti i sistemi hanno /dev/fd/x.


Molto bella! In effetti i miei test non ti servono xcon bash; ulteriormente con zsh puoi semplicemente fare reade funziona (anche se non capisco perché!). Questo trucco è specifico di Linux o funziona su tutti i sistemi * nix?
a3nm,

@ a3nm, se lo fai readda solo, leggerà da stdin. Quindi, se è il terminale, leggerà ciò che si digita fino a quando non si preme Invio.
Stéphane Chazelas,

certo, capisco cosa fa leggere. Quello che non capisco è perché leggere dal terminale con read in un processo in background sta bloccando con bash ma non con zsh.
a3nm,

@ a3nm, non sono sicuro di cosa intendi. Cosa intendi con ciò che puoi fare reade funziona ?
Stéphane Chazelas,

Sto dicendo che con zsh puoi semplicemente fare read | program > outpute funziona allo stesso modo di quello che hai suggerito. (E non capisco perché.)
a3nm,

0

La read soluzione di Stéphane Chazelas funziona anche su Mac OS X se viene aperta una lettura fd /dev/fd/1.

# using bash on Mac OS X
# -bash: /dev/fd/1: Permission denied
read x </dev/fd/1 | cat >/dev/null
echo ${PIPESTATUS[*]}   #  1 0

exec 3<&- 3</dev/fd/1
read x 0<&3 | cat >/dev/null
echo ${PIPESTATUS[*]}   #  0 0

Per poter uccidere tail -f /dev/nullin uno script (con SIGINT, per esempio) è necessario mettere in background il tailcomando e wait.

#!/bin/bash
# ctrl-c will kill tail and exit script
trap 'trap - INT; kill "$!"; exit' INT
exec tail -f /dev/null & wait $!

-2

Reindirizzamento /dev/zerocome input standard!

program < /dev/zero > output &

9
Ciò darebbe al suo programma un numero infinito di zero byte ... che, purtroppo, lo renderebbe occupato.
Jander,

1
Questo non è vero jander, / dev / zero non si chiuderà mai, tenendo aperta la catena di tubi. Tuttavia, il poster dice che non accetta lo stdin, quindi nessuno zero verrà mai trasferito al programma. Questo non è affatto un circuito affollato, è una pura attesa.
sillyMunky,

2
scusa, OP usa stdin, quindi questo cancellerà il suo input e trarrà da / dev / zero. Dovrei leggere due volte la prossima volta! Se OP non stesse usando stdin, questa sarebbe la soluzione più elegante che abbia mai visto e non sarebbe un'attesa impegnativa.
sillyMunky,
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.