2012-01-27 14 views
8

Utilizzo di bash Spesso desidero ottenere le intestazioni di un file csv di grandi dimensioni e cercare il resto per una voce specifica. Lo faccio come segue.Perché la presa dello stdin da un file differisce dal riceverlo su una pipe?

$ (head -1; grep mike) < tmp.csv 
name,age,favourite colour 
mike,38,blue 

Ma prendendo l'input dal gatto, o qualsiasi altro comando non funziona - sembra grep non viene mai superato il resto del file.

$ cat tmp.csv | (head -1; grep mike) 
name,age,favourite colour 

Perché c'è un comportamento diverso in questi due casi?

+0

'wc -l test.txt' -output-> 3' (testa -1>/dev/null; wc -l) 2.! – danihp

+0

Ho alcune teorie, ma non riesco a riprodurlo con bash 3.2.48 (Darwin). Che versione stai usando? –

+0

Questo è GNU bash, versione 3.2.25 (1) -release (x86_64-redhat-linux-gnu) su una macchina rhel 5.6. – tlrrd

risposta

7

La differenza tra la lettura da un tubo e la lettura da un file è che si può lseek su un file, ma non su un tubo.

Il comportamento qui appare (come visto attraverso strace) come se provenisse da head, non bash. head leggerà un buffer e troverà il numero appropriato di linee, quindi lseek all'indietro fino al punto in cui è terminata la riga dell'ultima uscita, lasciando l'handle del file aperto in quel punto. Come sopra, questo funziona se sta leggendo un file, ma non se sta leggendo da una pipe.

Non riesco a pensare di qualsiasi caso altro di quello che stai facendo, dove questo comportamento in head ha un senso, ma è così. Impara qualcosa di nuovo ogni giorno, ti dico ...

+0

+1 Ottima risposta, ma è solo pazzesco. – cmbuckley

0

Non riesco a riprodurlo in modo affidabile con bash 3.2.48. Entrambi o riescono o entrambi falliscono. Ma la ragione sottostante per i fallimenti è la dimensione del file.

cat legge un buffer (4k-64k a seconda del sistema) e lo passa sul tubo. head consuma l'intero buffer e quindi esce. grep ha quindi accesso al file dopo la dimensione del buffer. Sul mio sistema, posso usare la tua pipe solo su grep oltre un buffer nel file (così posso grep cose alla fine di un file lungo, ma non all'inizio dopo l'utilizzo di head).

È possibile che le versioni successive di bash ottimizzino l'operatore < (ma non lo cat) per consentire al proprio trucco di funzionare, ma non credo che questo sia un comportamento supportato.

+1

No, non è sicuramente la taglia. http://sprunge.us/SXCO –

3

Molto strano. Si consiglia di non fare affidamento su questo comportamento non documentato, usare qualcosa come questo, invece:

sed -n '1p;/mike/p' tmp.csv 
+2

Non è un comportamento non documentato. Tutto funziona come previsto (vedi risposta agli ottusi del male). È solo una cattiva pratica - usare la costruzione '(command1; command2)' per leggere i dati dallo standard input comune perché il secondo dipende dal comportamento del primo. Quindi hai ragione anche con il tuo comando. –

Problemi correlati