Quindi volevo contribuire con una risposta come quella di Lesmana, ma penso che la mia sia forse una soluzione pura-Bourne-shell un po 'più semplice e leggermente più vantaggiosa:
# You want to pipe command1 through command2:
exec 4>&1
exitstatus=`{ { command1; printf $? 1>&3; } | command2 1>&4; } 3>&1`
# $exitstatus now has command1's exit status.
Penso che questo sia meglio spiegato dall'interno - comando1 eseguirà e stamperà il suo normale output su stdout (descrittore di file 1), quindi una volta fatto, printf eseguirà e stamperà il codice di uscita di icommand1 sul suo stdout, ma lo stdout viene reindirizzato a descrittore di file 3.
Mentre command1 è in esecuzione, il suo stdout viene reindirizzato a command2 (l'output di printf non arriva mai a command2 perché lo inviamo al descrittore di file 3 anziché a 1, che è ciò che legge la pipe). Quindi reindirizziamo l'output di command2 al descrittore di file 4, in modo che rimanga anche fuori dal descrittore di file 1 - perché vogliamo che il descrittore di file 1 sia libero per un po 'più tardi, perché riporteremo l'output di printf sul descrittore di file 3 nel descrittore di file 1 - perché questo è ciò che il comando sostituzione (i backtick) catturerà ed è ciò che verrà inserito nella variabile.
L'ultimo aspetto magico è che per prima cosa exec 4>&1
abbiamo fatto un comando separato: apre il descrittore di file 4 come copia dello stdout della shell esterna. La sostituzione dei comandi catturerà tutto ciò che è scritto sullo standard dalla prospettiva dei comandi al suo interno - ma poiché l'output di command2 sta andando a descrivere il descrittore di file 4 per quanto riguarda la sostituzione dei comandi, la sostituzione dei comandi non lo cattura - tuttavia una volta ottiene "fuori" dalla sostituzione del comando, in realtà sta ancora andando al descrittore di file complessivo dello script 1.
( exec 4>&1
Deve essere un comando separato perché a molte shell comuni non piace quando si tenta di scrivere su un descrittore di file all'interno di una sostituzione comando, che viene aperto nel comando "esterno" che sta usando la sostituzione. Quindi questo è il modo portatile più semplice per farlo.)
Puoi guardarlo in un modo meno tecnico e più giocoso, come se gli output dei comandi si saltassero l'un l'altro: command1 si dirige verso command2, quindi l'output di printf salta sul comando 2 in modo che command2 non lo catturi, e quindi l'output del comando 2 passa sopra e fuori dalla sostituzione dei comandi proprio come printf atterra appena in tempo per essere catturato dalla sostituzione in modo che finisca nella variabile, e l'output di command2 continui nel suo modo felice di essere scritto nell'output standard, proprio come in un tubo normale.
Inoltre, a quanto ho capito, $?
conterrà comunque il codice di ritorno del secondo comando nella pipe, poiché assegnazioni variabili, sostituzioni di comandi e comandi composti sono tutti effettivamente trasparenti al codice di ritorno del comando al loro interno, quindi lo stato di restituzione di command2 dovrebbe essere propagato - questo, e non dovendo definire una funzione aggiuntiva, è il motivo per cui penso che questa potrebbe essere una soluzione un po 'migliore di quella proposta da lesmana.
Secondo le avvertenze lesmana, è possibile che command1 finisca per usare i descrittori di file 3 o 4, quindi per essere più robusto, dovresti:
exec 4>&1
exitstatus=`{ { command1 3>&-; printf $? 1>&3; } 4>&- | command2 1>&4; } 3>&1`
exec 4>&-
Nota che nel mio esempio uso i comandi composti, ma i subshells (usare al ( )
posto di { }
funzionerà anche, anche se potrebbe essere meno efficiente).
I comandi ereditano i descrittori di file dal processo che li avvia, quindi l'intera seconda riga erediterà il descrittore di file quattro e il comando composto seguito da 3>&1
erediterà il descrittore di file tre. Quindi si 4>&-
assicura che il comando composto interno non erediterà il descrittore di file quattro e 3>&-
che non erediterà il descrittore di file tre, quindi command1 ottiene un ambiente più "pulito" e più standard. Potresti anche spostare l'interno 4>&-
vicino al 3>&-
, ma immagino perché non limitarne il più possibile il campo di applicazione.
Non sono sicuro di quanto spesso le cose utilizzino direttamente il descrittore di file tre e quattro - penso che la maggior parte delle volte i programmi utilizzino syscalls che restituiscono descrittori di file non utilizzati al momento, ma a volte il codice scrive direttamente sul descrittore di file 3, I suppongo (potrei immaginare un programma che controlla un descrittore di file per vedere se è aperto e usarlo se lo è, o comportarsi diversamente di conseguenza se non lo è). Quindi quest'ultimo è probabilmente il migliore da tenere a mente e utilizzare per casi di carattere generale.