GNU/Linux >> Linux Esercitazione >  >> Linux

L'output di sostituzione del processo è fuori servizio?

Il

echo one; echo two > >(cat); echo three; 

il comando fornisce un output imprevisto.

Ho letto questo:come viene implementata la sostituzione del processo in bash? e molti altri articoli sulla sostituzione dei processi su Internet, ma non capisco perché si comporti in questo modo.

Risultato previsto:

one
two
three

Risultato reale:

prompt$ echo one; echo two > >(cat); echo three;
one
three
prompt$ two

Inoltre, questi due comandi dovrebbero essere equivalenti dal mio punto di vista, ma non lo fanno:

##### first command - the pipe is used.
prompt$ seq 1 5 | cat
1
2
3
4
5
##### second command - the process substitution and redirection are used.
prompt$ seq 1 5 > >(cat)
prompt$ 1
2
3
4
5

Perché penso che dovrebbero essere gli stessi? Perché entrambi connettono il seq output al cat input attraverso la pipe anonima – Wikipedia, Processo di sostituzione.

Domanda: Perché si comporta in questo modo? Dov'è il mio errore? La risposta esauriente è desiderata (con spiegazione di come bash lo fa sotto il cofano).

Risposta accettata:

Sì, in bash come in ksh (da dove proviene la funzione), i processi all'interno della sostituzione del processo non sono attesi (prima di eseguire il comando successivo nello script).

per un <(...) uno, di solito va bene come in:

cmd1 <(cmd2)

la shell attenderà cmd1 e cmd1 in genere attende cmd2 in virtù della lettura fino alla fine del file sulla pipe che viene sostituita, e quella fine del file si verifica in genere quando cmd2 muore. Questo è lo stesso motivo per cui diverse shell (non bash ) non preoccuparti di aspettare cmd2 in cmd2 | cmd1 .

Per cmd1 >(cmd2) , tuttavia, in genere non è così, poiché è più cmd2 che in genere attende cmd1 lì quindi generalmente uscirà dopo.

È stato risolto in zsh che attende cmd2 lì (ma non se lo scrivi come cmd1 > >(cmd2) e cmd1 non è integrato, usa {cmd1} > >(cmd2) invece come documentato).

ksh non attende per impostazione predefinita, ma ti consente di aspettarlo con wait builtin (rende anche il pid disponibile in $! , anche se ciò non aiuta se esegui cmd1 >(cmd2) >(cmd3) )

rc (con il cmd1 >{cmd2} sintassi), come ksh tranne per il fatto che puoi ottenere i pid di tutti i processi in background con $apids .

es (anche con cmd1 >{cmd2} ) attende cmd2 come in zsh e attende anche cmd2 in <{cmd2} reindirizzamenti del processo.

bash fa il pid di cmd2 (o più esattamente della subshell poiché esegue cmd2 in un processo figlio di quella subshell anche se è l'ultimo comando presente) disponibile in $! , ma non ti fa aspettare.

Se devi usare bash , puoi aggirare il problema utilizzando un comando che attenderà entrambi i comandi con:

{ { cmd1 >(cmd2); } 3>&1 >&4 4>&- | cat; } 4>&1

Ciò rende entrambi cmd1 e cmd2 hanno il loro fd 3 aperto su una pipe. cat attenderà la fine del file all'altra estremità, quindi in genere uscirà solo quando entrambi cmd1 e cmd2 sono morti. E la shell aspetterà quel cat comando. Potresti vederlo come una rete per catturare la terminazione di tutti i processi in background (puoi usarlo per altre cose avviate in background come con & , coproc o anche comandi in background a condizione che non chiudano tutti i descrittori di file come fanno in genere i daemon).

Correlati:cosa fa perdere le autorizzazioni ai file?

Nota che grazie a quel processo di subshell sprecato menzionato sopra, funziona anche se cmd2 chiude il suo fd 3 (i comandi di solito non lo fanno, ma alcuni come sudo o ssh fare). Versioni future di bash potrebbe eventualmente fare l'ottimizzazione lì come in altre shell. Allora avresti bisogno di qualcosa come:

{ { cmd1 >(sudo cmd2; exit); } 3>&1 >&4 4>&- | cat; } 4>&1

Per assicurarti che ci sia ancora un processo di shell aggiuntivo con quell'fd 3 aperto in attesa di quel sudo comando.

Nota che cat non leggerà nulla (poiché i processi non scrivono sul loro fd 3). Serve solo per la sincronizzazione. Farà solo un read() chiamata di sistema che alla fine restituirà senza nulla.

Puoi effettivamente evitare di eseguire cat utilizzando una sostituzione di comando per eseguire la sincronizzazione della pipe:

{ unused=$( { cmd1 >(cmd2); } 3>&1 >&4 4>&-); } 4>&1

Questa volta, è la shell invece di cat che sta leggendo dalla pipe la cui altra estremità è aperta su fd 3 di cmd1 e cmd2 . Stiamo utilizzando un'assegnazione variabile, quindi lo stato di uscita di cmd1 è disponibile in $? .

Oppure potresti eseguire manualmente la sostituzione del processo e quindi potresti persino utilizzare sh del tuo sistema poiché diventerebbe la sintassi della shell standard:

{ cmd1 /dev/fd/3 3>&1 >&4 4>&- | cmd2 4>&-; } 4>&1

tuttavia, come notato in precedenza, non tutti i sh le implementazioni attenderebbero cmd1 dopo cmd2 è finito (anche se è meglio del contrario). Quella volta, $? contiene lo stato di uscita di cmd2; anche se bash e zsh crea cmd1 Stato di uscita disponibile in ${PIPESTATUS[0]} e $pipestatus[1] rispettivamente (vedi anche il pipefail opzione in alcune shell quindi $? può segnalare il guasto di componenti del tubo diversi dall'ultimo)

Nota che yash ha problemi simili con il suo processo di reindirizzamento caratteristica. cmd1 >(cmd2) verrebbe scritto cmd1 /dev/fd/3 3>(cmd2) là. Ma cmd2 non è atteso e non puoi usare wait aspettare anche lui e il suo pid non viene reso disponibile nel $! anche variabile. Utilizzeresti le stesse soluzioni alternative di bash .


Linux
  1. Output dell'"ultimo" comando?

  2. Come modificare il reindirizzamento dell'output di un processo in esecuzione?

  3. Il valore massimo dell'ID di processo?

  4. Reindirizzamento di un output di una subshell a un processo?

  5. Esegui processo con output in tempo reale in PHP

Diff dove le linee sono per lo più le stesse ma fuori servizio?

Il modo portatile (posix) per ottenere la sostituzione del processo?

SIGTERM vs SIGKILL:qual è la differenza?

Sostituzione del processo:un modo non comune ma avanzato per il reindirizzamento di input/output in Linux

Linux:scopri quale processo sta usando tutta la RAM?

Scopri se il sistema operativo è in esecuzione in un ambiente virtuale