Come ottengo l'output e il valore di uscita di una subshell quando utilizzo "bash -e"?


70

Considera il seguente codice

outer-scope.sh

#!/bin/bash
set -e
source inner-scope.sh
echo $(inner)
echo "I thought I would've died :("

inner-scope.sh

#!/bin/bash
function inner() { echo "winner"; return 1; }

Sto cercando di outer-scope.shuscire quando una chiamata inner()fallisce. Poiché $()invoca una sotto-shell, ciò non accade.

In quale altro modo posso ottenere l'output di una funzione preservando il fatto che la funzione possa uscire con un codice di uscita diverso da zero?

Risposte:


102

$()conserva lo stato di uscita; devi solo usarlo in una dichiarazione che non ha uno stato proprio, come un compito.

output = $ (interno)

Dopodiché, $?conterrebbe lo stato di uscita di innere sarà possibile utilizzare tutti i tipi di controlli per esso:

output=$(inner) || exit $?
echo $output

O:

if ! output=$(inner); then
    exit $?
fi
echo $output

O:

if output=$(inner); then
    echo $output
else
    exit $?
fi

(Nota: un nudo exitsenza argomenti equivale a exit $?- cioè, esce con l'ultimo stato di uscita del comando. Ho usato il secondo modulo solo per chiarezza.)


Inoltre, per la cronaca: sourcein questo caso non è completamente correlato. Puoi semplicemente definire inner()nel outer-scope.shfile, con gli stessi risultati.


Perché è così, anche se $? contiene lo stato di uscita di $ () che lo script non esce automaticamente (dato che -e è impostato)? EDIT: non importa, penso che tu abbia risposto alle mie domande, grazie!
jabalsad,

Non ne sono sicuro. (Non ho provato nessuno dei precedenti.) Ma ci sono alcune restrizioni su -e, tutte spiegate nella manpage di bash; inoltre, se lo stai chiedendo echo $(), potrebbe essere perché i codici di uscita dei sottotitoli vengono ignorati quando la riga - il echocomando - ha un suo codice di uscita (di solito 0).
Grawity

1
Hmm, quando scrivo, if ! $(exit 1) ; then echo $?; fiottengo 0. Non sono sicuro che ifsia la strada da percorrere se è necessario preservare quel valore di uscita.
Ron Burk,

@grawity - Funziona con Dash? Sto cercando di aggirare alcuni fastidiosi comportamenti di Autoconf (vale a dire, Autoconf che segnala il successo quando Sun o il compilatore IBM stampano un'opzione illegale sul terminale).
jww

3
if ! output=$(inner); then exit $?; fiuscirà con un codice di ritorno pari a 0 perché $?fornirà il codice di ritorno !anziché il codice di ritorno di inner. Potresti ottenere il comportamento desiderato con, if output=$(inner); then : ; else exit $?; fima questo è ovviamente più dettagliato
SJL

26

Vedi BashFAQ / 002 :

Se si desidera entrambi (output e stato di uscita):

output=$(command)
status=$? 

Un caso speciale

Nota su un caso complicato con variabili locali di funzione, confronta il seguente codice:

f() { local    v=$(echo data; false); echo output:$v, status:$?; }
g() { local v; v=$(echo data; false); echo output:$v, status:$?; }

Otterremo:

$ f     # fooled by 'local' with inline initialization
output:data, status:0

$ g     # a good one
output:data, status:1

Perché?

Quando l'output di una subshell viene utilizzato per inizializzare una localvariabile, lo stato di uscita non è più della subshell, ma del comando , che molto probabilmente lo sarà .local 0

Vedi anche https://stackoverflow.com/a/4421282/537554


3
Anche se questo non ha veramente risposto alla domanda, questo mi è tornato utile oggi, quindi +1.
fourpastmidnight

1
Lo stato di uscita per i comandi bash è sempre quello dell'ultimo comando eseguito. Quando trascorriamo così tanto tempo in lingue fortemente tipizzate è facile dimenticare che "local" non è un identificatore di tipo ma solo un altro comando. Grazie per aver ripetuto questo punto qui, mi ha aiutato oggi.
markeissler,

2
Caspita, ho riscontrato questo preciso problema proprio ora e tu l'hai chiarito. Grazie!
krb686,

4
#!/bin/bash
set -e
source inner-scope.sh
foo=$(inner)
echo $foo
echo "I thought I would've died :("

Aggiungendo echo, la subshell non è indipendente (non viene controllata separatamente) e non si interrompe. L'assegnazione elude questo problema.

Puoi anche farlo e reindirizzare l'output su un file, per elaborarlo in seguito.

tmpfile=$( mktemp )
inner > $tmpfile
cat $tmpfile
rm $tmpfile

Ovviamente, il file $ tmpf esiste ancora nella seconda variante ...
Daniel Beck
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.