2015-01-28 23 views
5

Entrare in bash, mi piace, ma sembra che ci siano un sacco di sottigliezze che finiscono per fare una grande differenza in termini di funzionalità, e quant'altro, in ogni caso ecco la mia domanda:Bash uscita comando tubazioni in ciclo somma

I conoscere questo funziona:

total=0 
for i in $(grep number some.txt | cut -d " " -f 1); do 
    ((total+=i)) 
done 

Ma perché non fa questo ?:

grep number some.txt | cut -d " " -f 1 | while read i; do ((total+=i)); done 

some.txt:

1 number 
2 number 
50 number 

sia il ciclo for che il ciclo while ricevono 1, 2 e 50 separatamente, ma il ciclo for mostra la variabile totale che è 53 alla fine, mentre nel codice ciclo while rimane semplicemente a zero. So che ci sono alcune conoscenze fondamentali che mi mancano qui, per favore aiutatemi.

Ho anche non avere le differenze di tubazioni, per esempio Se corro

grep number some.txt | cut -d " " -f 1 | while read i; echo "-> $i"; done 

ottengo i risultati attesi

-> 1 
-> 2 
-> 50 

Ma se correre in questo modo

while read i; echo "-> $i"; done <<< $(grep number some.txt | cut -d " " -f 1) 

quindi l'uscita diventa

-> 1 2 50 

Questo mi sembra strano dal momento che grep emette il risultato in righe separate. Come se questo non era ambiguo, se avevo un file con solo numeri 1 2 3 in linee separate, e corse

while read i; echo "-> $i"; done < someother.txt 

L'uscita verrebbe stampato dall'eco in linee differenti, come previsto nella esempio precedente. So che < è per i file e < < < per gli output di comando, ma perché esiste questa differenza di linea?

In ogni caso, speravo che qualcuno potesse chiarire la questione, grazie per il vostro tempo!

+0

Come semplice esempio del problema nella seconda domanda prova: 'a = $ 'pippo \ nbar'; echo $ a; echo ===; echo "$ a; echo ===; cat <<< $ a; echo ===; cat <<<" $ a "'. –

+0

Puoi anche calcolare la somma della prima colonna usando awk: 'awk '{a + = $ 1} END {print a} 'some.txt' –

risposta

6
grep number some.txt | cut -d " " -f 1 | while read i; do ((total+=i)); done 

Ogni comando in una pipeline viene eseguito in una subshell. Ciò significa che quando si inserisce il ciclo while read in una pipeline, tutte le assegnazioni di variabili vengono perse.

See: BashFAQ 024 - "I set variables in a loop that's in a pipeline. Why do they disappear after the loop terminates? Or, why can't I pipe data to read?"

while read i; echo "-> $i"; done <<< "$(grep number some.txt | cut -d " " -f 1)" 

Per preservare newlines grep 's, aggiungere le virgolette. In caso contrario, il risultato di $(...) è soggetto alla suddivisione in parole che collassa tutti gli spazi bianchi in spazi singoli.

+2

L'altra alternativa è 'set + m; shopt -s lastpipe', ma questo è davvero fangoso per le acque. –

+0

Bene, questo chiarisce le cose! Grazie! – acib708