Eu tenho um arquivo de texto:
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
Quero pegar a 2ª e a 4ª palavra de cada linha assim:
1657 19.6117
1410 18.8302
3078 18.6695
2434 14.0508
3129 13.5495
Estou usando este código:
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
Funciona, mas é muito complicado e leva muito tempo para processar arquivos de texto longos.
Existe uma maneira mais simples de fazer isso?
Respostas:
iirc:
cat filename.txt | awk '{ print $2 $4 }'
ou, conforme mencionado nos comentários:
awk '{ print $2 $4 }' filename.txt
fonte
awk '{print $2,$4}' filename.txt
é melhor (sem tubo, apenas um programa chamado)cat
em meus scripts bash em vez de especificar um nome de arquivo, porque a sobrecarga é mínima e porque a sintaxecat ... | ... > ...
mostra muito bem o que é a entrada e para onde vai a saída. Você está certo, não é realmente necessário aqui.< input awk '{ print $2 $4 }' > output
com esse propósito.Você pode usar o
cut
comando:cut -d' ' -f3,5 < datafile.txt
estampas
a
-d' '
- quer dizer, usespace
como um delimitador-f3,5
- pegue e imprima a 3ª e 5ª colunaO
cut
é muito mais rápido para arquivos grandes como uma solução de shell pura. Se o seu arquivo for delimitado com vários espaços em branco, você pode removê-los primeiro, como:sed 's/[\t ][\t ]*/ /g' < datafile.txt | cut -d' ' -f3,5
onde o (gnu) sed substituirá qualquer caractere
tab
ouspace
por um únicospace
.Para uma variante - aqui está uma solução perl também:
perl -lanE 'say "$F[2] $F[4]"' < datafile.txt
fonte
Para fins de integridade:
while read _ _ one _ two _; do echo "$one $two" done < file.txt
Em vez de
_
uma variável arbitrária (comojunk
), também pode ser usada. A questão é apenas extrair as colunas.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
fonte
Mais uma variante simples -
$ while read line do set $line # assigns words in line to positional parameters echo "$3 $5" done < file
fonte
Se o seu arquivo contém n linhas, então seu script tem que ler o arquivo n vezes; portanto, se você dobrar o comprimento do arquivo, quadruplicará a quantidade de trabalho que seu script faz - e quase todo esse trabalho é simplesmente jogado fora, já que tudo o que você quer fazer é percorrer as linhas em ordem.
Em vez disso, a melhor maneira de percorrer as linhas de um arquivo é usar um
while
loop, com o comando de condição sendo oread
embutido: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
No seu caso, como você deseja dividir a linha em um array, e o
read
builtin realmente tem suporte especial para preencher uma variável de array, que é o que você deseja, você pode escrever:while read -r -a line ; do echo ""${line[1]}" "${line[3]}"" >> out.txt done < /path/of/my/text
ou melhor ainda:
while read -r -a line ; do echo "${line[1]} ${line[3]}" done < /path/of/my/text > out.txt
No entanto, para o que você está fazendo, você pode apenas usar o
cut
utilitário:cut -d' ' -f2,4 < /path/of/my/text > out.txt
(ou
awk
, como sugere Tom van der Woerdt, ouperl
, ou mesmosed
).fonte
read
sobrecut
porque é robusta contra vários espaços entre os campos e você não precisa de magia matriz:while read word1 word2 word3 word4 rest; do doSomethingWith $word2 $word4; done
Se você estiver usando dados estruturados, isso tem o benefício adicional de não chamar um processo shell extra para ser executado
tr
e /cut
ou algo assim. ...(Claro, você vai querer se proteger contra entradas ruins com alternativas condicionais e sãs.)
... while read line ; do lineCols=( $line ) ; echo "${lineCols[0]}" echo "${lineCols[1]}" done < $myFQFileToRead ; ...
fonte