Prendi l'ennesima colonna in un file di testo


85

Ho 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 la seconda e la quarta parola di ogni riga in questo modo:

1657 19.6117
1410 18.8302
3078 18.6695
2434 14.0508
3129 13.5495

Sto 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 file di testo lunghi.

C'è un modo più semplice per farlo?


1
2a parola di ogni riga chiamata semplicemente 2a colonna!
Bernard

Risposte:


127

iirc:

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

oppure, come menzionato nei commenti:

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

16
UUOC !!! awk '{print $2,$4}' filename.txtè meglio (niente pipe, solo un programma chiamato)
blu

5
@blue Uso spesso catnei miei script bash invece di specificare un nome di file, perché l'overhead è minimo e perché la sintassi cat ... | ... > ...mostra davvero bene qual è l'input e dove va l'output. Hai ragione però, in realtà non è necessario qui.
Tom van der Woerdt

8
@TomvanderWoerdt: a volte scrivo < input awk '{ print $2 $4 }' > outputper questo scopo.
ruakh

68

Puoi usare il cutcomando:

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

stampe

1657 19.6117
1410 18.8302
3078 18.6695
2434 14.0508
3129 13.5495

il

  • -d' '- significa, usa spacecome delimitatore
  • -f3,5 - prendere e stampare la 3a e 5a colonna

Il cutè molto più veloce di file di grandi dimensioni come soluzione shell pura. Se il tuo file è delimitato da più spazi, puoi prima rimuoverli, ad esempio:

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

dove (gnu) sed sostituirà qualsiasi carattere tabo spacecon un singolo space.

Per una variante, ecco anche una soluzione perl:

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

1
Funziona bene ... se ti viene garantito quel numero di spazi su ogni riga, esattamente ... :)
rogerdpack

24

Per amor di completezza:

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

Invece di _una variabile arbitraria (come junk) può essere utilizzata anche. Il punto è solo 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

Bello, leggibile e non sono necessari perls / awks / altri, tutto in una shell da builtin.
Petr Matousu,

6

Un'altra semplice variante:

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

4

Se il file contiene n righe, lo script deve leggere il file n volte; quindi se raddoppi la lunghezza del file, quadruplichi la quantità di lavoro svolto dallo script e quasi tutto il lavoro viene semplicemente gettato via, poiché tutto ciò che vuoi fare è scorrere le righe in ordine.

Invece, il modo migliore per eseguire un ciclo sulle righe di un file è utilizzare un whileciclo, con il comando condition come readincorporato:

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 vuoi dividere la riga in un array e il readbuiltin ha effettivamente un supporto speciale per popolare una variabile di array, che è quello che vuoi, puoi 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 puoi semplicemente usare l' cututilità:

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

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


preferirebbe readsopra cutperché è robusta contro molteplici spazi tra i campi e non hai bisogno di magia matrice:while read word1 word2 word3 word4 rest; do doSomethingWith $word2 $word4; done
user829755

3

Se stai usando dati strutturati, questo ha l'ulteriore vantaggio di non invocare un processo shell aggiuntivo da eseguire tre / cuto qualcosa del genere. ...

(Ovviamente, ti consigliamo di proteggerti da input errati con condizionali e alternative sensate.)

...
while read line ; 
do 
    lineCols=( $line ) ;
    echo "${lineCols[0]}"
    echo "${lineCols[1]}"
done < $myFQFileToRead ; 
...
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.