2013-06-16 11 views
45

Ho un file di testo:Bash - Prendere colonna n in un file di testo

1 Q0 1657 1 19.6117 Exp 
1 Q0 1410 2 18.8302 Exp 
2 Q0 3078 1 18.6695 Exp 
2 Q0 2434 2 14.0508 Exp 
2 Q0 3129 3 13.5495 Exp 

voglio prendere 2 ° e 4 ° parola di ogni riga come questa:

1657 19.6117 
1410 18.8302 
3078 18.6695 
2434 14.0508 
3129 13.5495 

I' m usando questo codice

nol=$(cat "/path/of/my/text" | wc -l) 
x=1 
while [ $x -le "$nol" ] 
do 
line=($(sed -n "$x"p /path/of/my/text) 
echo ""${line[1]}" "${line[3]}"" >> out.txt 
x=$(($x + 1)) 
done 

Funziona ma è molto complicato e richiede molto tempo per elaborare lunghi file di testo. C'è un modo semplice per farlo? Grazie.

+0

seconda parola di ogni riga chiamato 2a colonna semplicemente! – Bernard

risposta

67

IIRC:

cat filename.txt | awk '{ print $2 $4 }' 

o, come detto nei commenti:

awk '{ print $2 $4 }' filename.txt 
+11

UUOC !!! 'awk '{print $ 2, $ 4}' filename.txt' è migliore (niente pipe, solo un programma chiamato) – blue

+2

@blue Spesso uso' cat' nei miei script bash invece di specificare un nome file, perché il sovraccarico è minimo e perché la sintassi 'cat ... | ...> ... 'mostra molto bene l'input e dove va l'output. Hai ragione, però, non è effettivamente necessario qui. –

+0

Grazie mille! cat mytext | awk '{print $ 2, $ 3}' funziona per me :) Grazie ancora. – mnrl

41

È possibile utilizzare il comando cut:

cut -d' ' -f3,5 < datafile.txt 

stampe

1657 19.6117 
1410 18.8302 
3078 18.6695 
2434 14.0508 
3129 13.5495 

il

  • -d' ' - media, utilizzare space come delimitatore
  • -f3,5 - prendono e 3 ° di stampa e 5 ° colonna

Il cut è molto più veloce per file di grandi dimensioni come un puro soluzione di guscio. Se il file è delimitato con più spazi bianchi, è possibile rimuoverli prima, come:

sed 's/[\t ][\t ]*/ /g' < datafile.txt | cut -d' ' -f3,5 

dove il (GNU) sed sostituirà tutti tab o space personaggi con un singolo space.

Per una variante - Ecco una soluzione perl troppo:

perl -lanE 'say "$F[2] $F[4]"' < datafile.txt 
4

Se il file contiene n linee, allora lo script deve leggere il file n volte; quindi se raddoppi la lunghezza del file, quadruplichi la quantità di lavoro eseguita dal tuo script — e quasi tutto il lavoro viene semplicemente gettato via, poiché tutto ciò che vuoi fare è ripetere il ciclo delle righe in ordine.

invece, il modo migliore per ciclo sopra le righe di un file è quello di utilizzare un ciclo while, con la condizione di comando è il read incorporato:

while IFS= read -r line ; do 
    # $line is a single line of the file, as a single string 
    : ... commands that use $line ... 
done < input_file.txt 

Nel tuo caso, dal momento che si desidera dividere la linea in un array, e il read integrato ha in realtà un supporto speciale per la compilazione di una variabile di matrice, che è ciò che si desidera, è possibile scrivere:

while read -r -a line ; do 
    echo ""${line[1]}" "${line[3]}"" >> out.txt 
done < /path/of/my/text 

o meglio ancora:

while read -r -a line ; do 
    echo "${line[1]} ${line[3]}" 
done </path/of/my/text> out.txt 

Tuttavia, per quello che stai facendo si può semplicemente utilizzare l'utility cut:

cut -d' ' -f2,4 </path/of/my/text> out.txt 

(o awk, come suggerisce Tom van der Woerdt, o perl, o anche sed).

+0

preferirebbe 'read' su' cut' perché è robusto contro più spazi tra i campi e non è necessario il magic array: 'while read word1 word2 word3 word4 rest; fai qualcosa Qualcosa con $ parola2 $ parola4; done' – user829755

+0

Grazie mille. leggere è l'idea migliore. – mnrl

+0

@ user151597: prego! – ruakh

14

Per ragioni di completezza:

while read _ _ one _ two _; do 
    echo "$one $two" 
done < file.txt 

Invece di _ una variabile arbitraria (come junk) può essere utilizzato come bene. Il punto è solo per estrarre le colonne.

Demo:

$ while read _ _ one _ two _; do echo "$one $two"; done < /tmp/file.txt 
1657 19.6117 
1410 18.8302 
3078 18.6695 
2434 14.0508 
3129 13.5495 
+0

Un bel trucco! – nbubis

4

Ancora una semplice variante -

$ while read line ; 
    do 
     set $line   # assigns words in line to positional parameters 
     echo "$3 $5" 
    done < file 
3

Se si utilizza i dati strutturati, questo ha il vantaggio di non invocare un processo shell in più per eseguire tr e/o cut o qualcosa del genere. ...

(Naturalmente, si vuole evitare ingressi cattivi con i condizionali e le alternative sane.)

... 
while read line ; 
do 
    lineCols=($line) ; 
    echo "${lineCols[0]}" 
    echo "${lineCols[1]}" 
done < $myFQFileToRead ; 
... 
Problemi correlati