Sì, 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 <(...)
uno, di solito va bene come in:
cmd1 <(cmd2)
la shell attenderà cmd1
e cmd1
in genere attenderà in cmd2
virtù della sua lettura fino alla fine del file sulla pipe che viene sostituita e tale fine del file si verifica in genere quando cmd2
muore. Questa è la stessa ragione per diverse shell (non bash
) non si preoccupano in attesa di cmd2
in cmd2 | cmd1
.
Per cmd1 >(cmd2)
, tuttavia, che non è generalmente il caso, come è più cmd2
che tipicamente attende cmd1
lì così sarà generalmente uscita dopo.
Ciò è stato risolto in zsh
quello che attende cmd2
lì (ma non se lo scrivi come cmd1 > >(cmd2)
e cmd1
non è incorporato, usa {cmd1} > >(cmd2)
invece come documentato ).
ksh
non aspetta per impostazione predefinita, ma ti consente di aspettarlo con il wait
built-in (rende anche disponibile il pid $!
, anche se ciò non aiuta se lo fai cmd1 >(cmd2) >(cmd3)
)
rc
(con la cmd1 >{cmd2}
sintassi), come ksh
se fosse possibile ottenere i pid di tutti i processi in background $apids
.
es
(anche con cmd1 >{cmd2}
) attende cmd2
like in zsh
e attende anche reindirizzamenti cmd2
in <{cmd2}
corso.
bash
rende disponibile il pid di cmd2
(o più esattamente della subshell mentre viene eseguito cmd2
in un processo figlio di quella subshell anche se è l'ultimo comando lì) disponibile $!
, ma non ti fa aspettare.
Se è necessario utilizzare bash
, è possibile 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 a una pipe. cat
attenderà per la fine del file all'altra estremità, così sarà in genere solo uscire quando entrambi cmd1
e cmd2
sono morti. E la shell attenderà quel cat
comando. Potresti vederlo come una rete per catturare la terminazione di tutti i processi in background (puoi usarlo per altre cose iniziate in background come con &
, coprocs o anche comandi che in background stessi a condizione che non chiudano tutti i loro descrittori di file come fanno normalmente i demoni ).
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 lo fanno sudo
o lo ssh
fanno). Le versioni future di bash
potrebbero eventualmente fare l'ottimizzazione lì come in altre shell. Quindi avresti bisogno di qualcosa come:
{ { cmd1 >(sudo cmd2; exit); } 3>&1 >&4 4>&- | cat; } 4>&1
Per assicurarsi che ci sia ancora un ulteriore processo di shell con quel fd 3 aperto in attesa di quel sudo
comando.
Nota che cat
non leggerà nulla (poiché i processi non scrivono sul loro fd 3). È lì solo per la sincronizzazione. Farà solo una read()
chiamata di sistema che tornerà senza nulla alla fine.
Puoi effettivamente evitare l'esecuzione 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 cat
che sta leggendo dalla pipe la cui altra estremità è aperta su fd 3 di cmd1
e cmd2
. Stiamo utilizzando un'assegnazione variabile per cui lo stato di uscita di cmd1
è disponibile in $?
.
Oppure potresti fare manualmente la sostituzione del processo, e quindi potresti anche usare il tuo sistema in sh
quanto diventerebbe la sintassi della shell standard:
{ cmd1 /dev/fd/3 3>&1 >&4 4>&- | cmd2 4>&-; } 4>&1
tuttavia, come notato in precedenza, non tutte le sh
implementazioni che aspetterebbero cmd1
dopo il cmd2
completamento (anche se è meglio del contrario). Quella volta, $?
contiene lo stato di uscita di cmd2
; sebbene bash
e zsh
rendere lo cmd1
stato di uscita disponibile in ${PIPESTATUS[0]}
e $pipestatus[1]
rispettivamente (vedere anche l' pipefail
opzione in alcune shell in modo da $?
poter segnalare il fallimento dei componenti del tubo diversi dall'ultimo)
Si noti che yash
ha problemi simili con la sua funzione di reindirizzamento del processo . cmd1 >(cmd2)
sarebbe scritto cmd1 /dev/fd/3 3>(cmd2)
lì. Ma cmd2
non è atteso e non puoi nemmeno usarlo wait
e il suo pid non è reso disponibile $!
nemmeno nella variabile. Useresti lo stesso lavoro come per bash
.