Pegue a enésima coluna em um arquivo de texto

86

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?

mnrl
fonte
1
2ª palavra de cada linha chamada 2ª coluna simplesmente!
Bernard

Respostas:

127

iirc:

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

ou, conforme mencionado nos comentários:

awk '{ print $2 $4 }' filename.txt
Tom van der Woerdt
fonte
16
UUOC !!! awk '{print $2,$4}' filename.txté melhor (sem tubo, apenas um programa chamado)
azul de
5
@blue Eu costumo usar catem meus scripts bash em vez de especificar um nome de arquivo, porque a sobrecarga é mínima e porque a sintaxe cat ... | ... > ...mostra muito bem o que é a entrada e para onde vai a saída. Você está certo, não é realmente necessário aqui.
Tom van der Woerdt
8
@TomvanderWoerdt: Às vezes escrevo < input awk '{ print $2 $4 }' > outputcom esse propósito.
ruakh
69

Você pode usar o cutcomando:

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

estampas

1657 19.6117
1410 18.8302
3078 18.6695
2434 14.0508
3129 13.5495

a

  • -d' ' - quer dizer, use space como um delimitador
  • -f3,5 - pegue e imprima a 3ª e 5ª coluna

O 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 tabou spacepor um único space.

Para uma variante - aqui está uma solução perl também:

perl -lanE 'say "$F[2] $F[4]"' < datafile.txt
jm666
fonte
1
Funciona bem ... se você tiver garantido esse número de espaços em cada linha, exatamente ... :)
rogerdpack
24

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
Johannes Weiss
fonte
Agradável, legível e sem necessidade de perls / awks / others, tudo em um único shell por builtins.
Petr Matousu
6

Mais uma variante simples -

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

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 whileloop, com o comando de condição sendo o readembutido:

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 readbuiltin 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 cututilitário:

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

(ou awk, como sugere Tom van der Woerdt, ou perl, ou mesmo sed).

Ruakh
fonte
prefere readsobre cutporque é 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
user829755
3

Se você estiver usando dados estruturados, isso tem o benefício adicional de não chamar um processo shell extra para ser executado tre / cutou 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 ; 
...
ingyhere
fonte