Weź n-tą kolumnę w pliku tekstowym

86

Mam plik tekstowy:

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

Chcę wziąć drugie i czwarte słowo z każdego wiersza w ten sposób:

1657 19.6117
1410 18.8302
3078 18.6695
2434 14.0508
3129 13.5495

Używam tego kodu:

 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

Działa, ale jest bardzo skomplikowany, a przetwarzanie długich plików tekstowych zajmuje dużo czasu.

Czy jest na to prostszy sposób?

mnrl
źródło
1
Drugie słowo w każdym wierszu zwane po prostu drugą kolumną!
Bernard

Odpowiedzi:

127

iirc:

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

lub, jak wspomniano w komentarzach:

awk '{ print $2 $4 }' filename.txt
Tom van der Woerdt
źródło
16
UUOC !!! awk '{print $2,$4}' filename.txtjest lepszy (bez potoku, tylko jeden program wywołany)
niebieski
5
@blue Często używam catw moich skryptach bash zamiast określać nazwę pliku, ponieważ narzut jest minimalny i ponieważ składnia cat ... | ... > ...naprawdę dobrze pokazuje, jakie jest wejście i gdzie idzie wyjście. Masz jednak rację, nie jest tu właściwie potrzebne.
Tom van der Woerdt
8
@TomvanderWoerdt: Czasami piszę < input awk '{ print $2 $4 }' > outputw tym celu.
ruakh
69

Możesz użyć cutpolecenia:

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

wydruki

1657 19.6117
1410 18.8302
3078 18.6695
2434 14.0508
3129 13.5495

the

  • -d' '- oznacza, użyj spacejako separatora
  • -f3,5 - weź i wydrukuj trzecią i piątą kolumnę

Jest cutto znacznie szybsze rozwiązanie dla dużych plików jako rozwiązanie w czystej powłoce. Jeśli plik jest rozdzielany wieloma spacjami, możesz je najpierw usunąć, na przykład:

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

gdzie (gnu) sed zamieni dowolny znak tablub spacena pojedynczy space.

Dla wariantu - tutaj jest również rozwiązanie Perl:

perl -lanE 'say "$F[2] $F[4]"' < datafile.txt
jm666
źródło
1
Działa dobrze ... jeśli masz zagwarantowaną liczbę spacji w każdym wierszu, dokładnie ... :)
rogerdpack
24

Ze względu na kompletność:

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

Zamiast _dowolnej zmiennej (takiej jak junk) można również użyć. Chodzi o to, aby wyodrębnić kolumny.

Próbny:

$ 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
Johannes Weiss
źródło
Ładny, czytelny i nie potrzeba żadnych perls / awks / innych, wszystko w jednej powłoce przez wbudowane.
Petr Matousu
6

Jeszcze jeden prosty wariant -

$ while read line
  do
      set $line          # assigns words in line to positional parameters
      echo "$3 $5"
  done < file
AKA11
źródło
4

Jeśli twój plik zawiera n wierszy, to twój skrypt musi odczytać plik n razy; więc jeśli podwoisz długość pliku, czterokrotnie zwiększysz ilość pracy wykonywanej przez skrypt - i prawie cała ta praca jest po prostu odrzucana, ponieważ wszystko, co chcesz zrobić, to pętla po liniach w kolejności.

Zamiast tego najlepszym sposobem na zapętlenie wierszy pliku jest użycie whilepętli, w której polecenie warunku jest readwbudowane:

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

W twoim przypadku, ponieważ chcesz podzielić linię na tablicę, a readwbudowana faktycznie ma specjalną obsługę zapełniania zmiennej tablicowej, co jest tym, czego chcesz, możesz napisać:

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

Lub jeszcze lepiej:

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

Jednak do tego, co robisz, możesz po prostu użyć cutnarzędzia:

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

(lub awk, jak sugeruje Tom van der Woerdt, lub perl, a nawet sed).

ruakh
źródło
wolałby readwięcej, cutponieważ jest odporny na wiele spacji między polami i nie potrzebujesz magii tablic:while read word1 word2 word3 word4 rest; do doSomethingWith $word2 $word4; done
user829755
3

Jeśli używasz danych strukturalnych, ma to dodatkową zaletę polegającą na tym, że nie wywołujesz dodatkowego procesu powłoki do uruchomienia tri / cutlub czegoś takiego. ...

(Oczywiście będziesz chciał chronić się przed złymi danymi wejściowymi za pomocą warunków warunkowych i rozsądnych alternatyw).

...
while read line ; 
do 
    lineCols=( $line ) ;
    echo "${lineCols[0]}"
    echo "${lineCols[1]}"
done < $myFQFileToRead ; 
...
ingyhere
źródło