2009-03-05 16 views
418

Ho uno script 'myscript' che emette la seguente:Catturare uscita di linea multipla in una variabile Bash

abc 
def 
ghi 

in un altro script, mi chiamano:

declare RESULT=$(./myscript) 

e $RESULT ottiene il valore

abc def ghi 

esiste un modo per memorizzare il risultato sia con le nuove righe, o con '\ n' personaggio così i c un output con 'echo -e'?

+1

mi sorprende. non hai $ (cat ./myscipt)? altrimenti mi sarei aspettato che provasse ad eseguire i comandi abc, def e ghi –

+0

@litb: sì, suppongo di sì; puoi anche usare $ (<./ myscript) per evitare di eseguire un comando. –

+2

(NB: i due commenti sopra si riferiscono a una revisione della domanda che è iniziata _Ho trovato uno script 'myscript' che contiene il seguente_, che ha portato alle domande.La revisione corrente della domanda (_Ho avuto uno script 'myscript' che emette il seguente_) rende i commenti superflui.Tuttavia, la revisione è del 2011-11-11, molto tempo dopo che sono stati fatti i due commenti –

risposta

784

In realtà, risultato contiene ciò che si vuole - di dimostrare:

echo "$RESULT" 

ciò che si mostra è quello che si ottiene da:

echo $RESULT 

Come notato nei commenti, la differenza è che (1) la versione a virgolette della variabile (echo "$RESULT") conserva la spaziatura interna del valore esattamente come è rappresentata nella variabile - newlines, tabs, più spazi e tutto - wherea s (2) la versione non quotata (echo $RESULT) sostituisce ogni sequenza di uno o più spazi, tabulazioni e nuove righe con un singolo spazio. Quindi (1) conserva la forma della variabile di input, mentre (2) crea una singola riga di output potenzialmente molto lunga con "parole" separate da singoli spazi (dove una "parola" è una sequenza di caratteri non di uno spazio bianco; Non ci sono alfanumerici in nessuna delle parole).

+59

@troelskn: la differenza è che (1) la versione a due virgolette di la variabile conserva la spaziatura interna del valore esattamente come è rappresentata nella variabile, newlines, tabs, più spazi vuoti e tutti, mentre (2) la versione non quotata sostituisce ogni sequenza di uno o più spazi, tab e newline con un singolo spazio. Quindi (1) conserva la forma della variabile di input, mentre (2) crea una singola riga di output potenzialmente molto lunga con "parole" separate da singoli spazi (dove una "parola" è una sequenza di caratteri non di uno spazio bianco; essere qualsiasi alfanumerici in una qualsiasi delle parole). –

+17

Per rendere la risposta più facile da capire: la risposta dice che echo "$ RESULT" conserva newline, mentre echo $ RESULT no. –

+0

Ciò non riesce a preservare le nuove linee e gli spazi iniziali in alcune situazioni. – CommaToast

72

Un altro trabocchetto con questo è che command substitution - $() - strisce che seguono i newline. Probabilmente non sempre importante, ma se si vuole veramente conservare esattamente quello che era uscita, si dovrà utilizzare un'altra linea e alcuni citando:

RESULTX="$(./myscript; echo x)" 
RESULT="${RESULTX%x}" 

Ciò è particolarmente importante se si vuole handle all possible filenames (per evitare comportamento indefinito come operare sul file sbagliato).

+3

+1 Questo è vero e cosa mancava dalla risposta di JonathanLeffler! –

+3

Ho dovuto lavorare per un po 'con una shell spezzata che non ha rimosso l'ultima nuova riga dalla [sostituzione dei comandi] (http://www.gnu.org/software/bash/manual/bash.html#Command-Substitution) (è _non_ [sostituzione del processo] (http://www.gnu.org/software/bash/manual/bash.html#Process-Substitution)) e ha rotto quasi tutto. Ad esempio, se hai fatto '' pwd = 'pwd'; ls $ pwd/$ file'', hai una nuova riga prima di '/', e il nome racchiuso tra virgolette non aiuta. È stato risolto rapidamente Questo era nel periodo 1983-5 su ICL Perq PNX; la shell non aveva '$ PWD' come variabile incorporata. –

0

Che ne dici di questo, leggerà ogni riga su una variabile e che può essere utilizzata successivamente! dicono uscita myscript viene reindirizzato a un file chiamato myscript_output

awk '{while ((getline var < "myscript_output") >0){print var;} close ("myscript_output");}' 
+4

Beh, non è bash, è awk. – vadipp

12

Oltre alla risposta data dal @ l0b0 Ho appena avuto la situazione in cui avevo bisogno di entrambi mantenere qualsiasi uscita a capo finale dallo script e controllo della codice di ritorno dello script. E il problema con la risposta di l0b0 è che "echo x" stava ripristinando $? di nuovo a zero ... così sono riuscito a venire con questa soluzione molto astuto:

RESULTX="$(./myscript; echo x$?)" 
RETURNCODE=${RESULTX##*x} 
RESULT="${RESULTX%x*}" 
11

Nel caso in cui siete interessati a linee specifiche, utilizzare un risultato-array:

declare RESULT=($(./myscript)) # (..) = array 
echo "First line: ${RESULT[0]}" 
echo "Second line: ${RESULT[1]}" 
echo "N-th line: ${RESULT[N]}" 
+2

Se ci sono spazi nelle linee, questo conterà i campi (contenuto tra gli spazi) piuttosto che le linee. – Liam

+3

questo non funziona su Dash o POSIX Shell – gpanda

+1

Si utilizzerà 'readarray' e si sostituisce il processo anziché la sostituzione di comando:' readarray -t RESULT <<(./myscript> '. – chepner

0

Dopo provando la maggior parte delle soluzioni qui, la cosa più semplice che ho trovato è stata l'ovvia: usare un file temporaneo. Non sono sicuro di cosa vuoi fare con il tuo output su più righe, ma puoi gestirlo riga per riga usando read.L'unica cosa che non si può veramente fare è attaccare tutto nella stessa variabile, ma per molti scopi pratici è molto più facile da gestire.

./myscript.sh > /tmp/foo 
while read line ; do 
    echo 'whatever you want to do with $line' 
done < /tmp/foo 

mod rapido per fargli fare l'azione richiesta:

result="" 
./myscript.sh > /tmp/foo 
while read line ; do 
    result="$result$line\n" 
done < /tmp/foo 
echo -e $result 

Nota questo aggiunge una linea supplementare. Se ci lavori sopra, puoi codificarci intorno, sono solo troppo pigro.


EDIT: Anche se questo caso funziona perfettamente, persone che leggono questo devono essere consapevoli che si può facilmente schiacciare il tuo stdin all'interno del ciclo while, dando così uno script che verrà eseguito una sola riga, stdin chiara, e l'uscita . Come ssh farò quello che penso? L'ho appena visto di recente, altri esempi di codice qui: https://unix.stackexchange.com/questions/24260/reading-lines-from-a-file-with-bash-for-vs-while

Un'altra volta! Questa volta con un filehandle diverso (stdin, stdout, stderr sono 0-2, quindi possiamo usare & 3 o più in bash).

result="" 
./test>/tmp/foo 
while read line <&3; do 
    result="$result$line\n" 
done 3</tmp/foo 
echo -e $result 

è possibile utilizzare anche mktemp, ma questo è solo un esempio di codice rapido. Uso per mktemp assomiglia:

filenamevar=`mktemp /tmp/tempXXXXXX` 
./test > $filenamevar 

Quindi utilizzare $ filenamevar come si farebbe il nome effettivo di un file. Probabilmente non ha bisogno di essere spiegato qui ma qualcuno si è lamentato nei commenti.

+0

Ho provato anche altre soluzioni, con il tuo primo suggerimento ho finalmente ottenuto il mio script funzionante –

+1

Downvote: questo è eccessivamente complesso e non riesce a evitare più comuni ['problemi di bash'] (http://mywiki.wooledge.org/BashPitfalls) . – tripleee

+0

Ya qualcuno mi ha parlato dello strano problema di filehandle con stdin l'altro giorno e io ero tipo "wow". Fammi aggiungere qualcosa in fretta. – user1279741

Problemi correlati