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?
Odpowiedzi:
iirc:
cat filename.txt | awk '{ print $2 $4 }'
lub, jak wspomniano w komentarzach:
awk '{ print $2 $4 }' filename.txt
źródło
awk '{print $2,$4}' filename.txt
jest lepszy (bez potoku, tylko jeden program wywołany)cat
w moich skryptach bash zamiast określać nazwę pliku, ponieważ narzut jest minimalny i ponieważ składniacat ... | ... > ...
naprawdę dobrze pokazuje, jakie jest wejście i gdzie idzie wyjście. Masz jednak rację, nie jest tu właściwie potrzebne.< input awk '{ print $2 $4 }' > output
w tym celu.Możesz użyć
cut
polecenia:cut -d' ' -f3,5 < datafile.txt
wydruki
the
-d' '
- oznacza, użyjspace
jako separatora-f3,5
- weź i wydrukuj trzecią i piątą kolumnęJest
cut
to 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
tab
lubspace
na pojedynczyspace
.Dla wariantu - tutaj jest również rozwiązanie Perl:
perl -lanE 'say "$F[2] $F[4]"' < datafile.txt
źródło
Ze względu na kompletność:
while read _ _ one _ two _; do echo "$one $two" done < file.txt
Zamiast
_
dowolnej zmiennej (takiej jakjunk
) 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
źródło
Jeszcze jeden prosty wariant -
$ while read line do set $line # assigns words in line to positional parameters echo "$3 $5" done < file
źródło
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
while
pętli, w której polecenie warunku jestread
wbudowane: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
read
wbudowana 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ć
cut
narzędzia:cut -d' ' -f2,4 < /path/of/my/text > out.txt
(lub
awk
, jak sugeruje Tom van der Woerdt, lubperl
, a nawetsed
).źródło
read
więcej,cut
ponieważ 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
Jeśli używasz danych strukturalnych, ma to dodatkową zaletę polegającą na tym, że nie wywołujesz dodatkowego procesu powłoki do uruchomienia
tr
i /cut
lub 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 ; ...
źródło