Como processar um arquivo de texto com várias colunas para obter outro arquivo de texto com várias colunas?

17

Eu tenho um arquivo de texto:

a   aa  aaa     b   bb  bbb     c   cc  ccc
d   dd  ddd     e   ee  eee     f   ff  fff
g   gg  ggg     h   hh  hhh     i   ii  iii
j   jj  jjj

Como posso processá-lo e obter um arquivo de 2 colunas como este:

a   aa
aaa b
bb  bbb
c   cc
ccc d
dd  ddd
e   ee
eee f
ff  fff
g   gg
ggg h
hh  hhh
i   ii
iii j
jj  jjj

Ou um arquivo de três colunas como este:

a   aa  aaa
b   bb  bbb
c   cc  ccc
d   dd  ddd
e   ee  eee
f   ff  fff
g   gg  ggg
h   hh  hhh
i   ii  iii
j   jj  jj

Eu prefiro obter a solução awk, mas outras soluções também são bem-vindas.

Apenas um aprendiz
fonte

Respostas:

1

Você também pode fazer isso com uma única chamada do GNU awk:

reshape.awk

# Set awk to split input at whitespace characters and
# use tab as the output field separator 
BEGIN {
  RS="[ \t\n]+"
  OFS="\t"
}

# Print using OFS or ORS based on the element index
{
  printf "%s", $1 (NR%n == 0 ? ORS : OFS)
}

# Append a missing new-line when last row is not full
END { 
  if( NR%n != 0) 
    printf "\n"
}

Execute-o assim:

awk -f reshape.awk n=2 infile

Ou como uma linha:

awk -v n=2 'BEGIN { RS="[ \t\n]+"; OFS="\t" } { printf "%s", $1 (NR%n == 0 ? ORS : OFS) } END { if( NR%n != 0) printf "\n" }' infile

Resultado:

a   aa
aaa b
bb  bbb
c   cc
ccc d
dd  ddd
e   ee
eee f
ff  fff
g   gg
ggg h
hh  hhh
i   ii
iii j
jj  jjj

Ou com n=3:

a   aa  aaa
b   bb  bbb
c   cc  ccc
d   dd  ddd
e   ee  eee
f   ff  fff
g   gg  ggg
h   hh  hhh
i   ii  iii
j   jj  jjj
Thor
fonte
Isso não é usado $1como string de formatoprintf ?
Wildcard
@Wildcard: Certo, é mais seguro de usar "%s", .... Atualizado
Thor
Obrigado por confirmar. :) A awkpropósito, o mesmo se aplica ao comando da sua outra resposta a esta pergunta.
Curinga
20

Coloque cada campo em uma linha e pós-coluna.

Cada campo em uma linha

tr

tr -s ' ' '\n' < infile

grep

grep -o '[[:alnum:]]*' infile

sed

sed 's/\s\+/\n/g' infile

ou mais portátil:

sed 's/\s\+/\
/g' infile

awk

awk '$1=$1' OFS='\n' infile

ou

awk -v OFS='\n' '$1=$1' infile

Columnate

colar

Para 2 colunas:

... | paste - -

Para 3 colunas:

... | paste - - -

etc.

sed

Para 2 colunas:

... | sed 'N; s/\n/\t/g'

Para 3 colunas:

... | sed 'N; N; s/\n/\t/g'

etc.

xargs

... | xargs -n number-of-desired-columns

Como xargscostuma /bin/echoimprimir, lembre-se de que os dados que parecem opções echoserão interpretados como tal.

awk

... | awk '{ printf "%s", $0 (NR%n==0?ORS:OFS) }' n=number-of-desired-columns OFS='\t'

pr

... | pr -at -number-of-desired-columns

ou

... | pr -at -s$'\t' -number-of-desired-columns

colunas (do pacote autogen)

... | columns -c number-of-desired-columns

Saída típica:

a   aa  aaa
b   bb  bbb
c   cc  ccc
d   dd  ddd
e   ee  eee
f   ff  fff
g   gg  ggg
h   hh  hhh
i   ii  iii
j   jj  jjj
Thor
fonte
2
Enterrada. +1 sir
Steven Penny
A xargslinha não deveria ligar echoou printf?
Curinga
1
@Wildcard: xargschamadas /bin/echopor padrão
Thor
1
Uau, eu não tinha ideia! É até especificado pelo POSIX . Obrigado!
Curinga
@Wildcard: O envio de dados para o xargsque parece opções para /bin/echocausar problemas ... eu adicionei um aviso.
Thor
9
$ sed -E 's/\s+/\n/g' ip.txt | paste - -
a   aa
aaa b
bb  bbb
c   cc
ccc d
dd  ddd
e   ee
eee f
ff  fff
g   gg
ggg h
hh  hhh
i   ii
iii j
jj  jjj

$ sed -E 's/\s+/\n/g' ip.txt | paste - - -
a   aa  aaa
b   bb  bbb
c   cc  ccc
d   dd  ddd
e   ee  eee
f   ff  fff
g   gg  ggg
h   hh  hhh
i   ii  iii
j   jj  jjj
Sundeep
fonte
9

Como o Wildcard apontou, isso só funcionará se o arquivo estiver bem formatado, pois não há caracteres especiais que o shell interprete como globs e você ficará satisfeito com as regras padrão de divisão de palavras. Se houver alguma dúvida sobre se seus arquivos serão "aprovados" nesse teste, não use essa abordagem.

Uma possibilidade seria usar printfpara fazê-lo como

printf '%s\t%s\n' $(cat your_file)

Isso fará a divisão de palavras no conteúdo your_filee os emparelhará e imprimirá com guias entre elas. Você pode usar mais %sseqüências de formato printfpara ter colunas extras.

Eric Renouf
fonte
1
Depende do arquivo que não contém caracteres especiais. Se houver, por exemplo, asteriscos (*), você obterá resultados muito inesperados.
Wildcard
4
perl -n0E 'say s/\s+/ ++$n % 4 ?"\t":"\n"/gre' file

(substitua 4 pelo número de colunas)

JJoao
fonte
4

rsUtilitário BSD (remodelar):

$ rs 0 2
a   aa  aaa     b   bb  bbb     c   cc  ccc
d   dd  ddd     e   ee  eee     f   ff  fff
g   gg  ggg     h   hh  hhh     i   ii  iii
j   jj  jjj
[Ctrl-D][Enter]
a    aa
aaa  b
bb   bbb
c    cc
ccc  d
dd   ddd
e    ee
eee  f
ff   fff
g    gg
ggg  h
hh   hhh
i    ii
iii  j
jj   jjj

0 2é linhas e colunas . Especificar 0significa "calcular linhas automaticamente a partir de colunas".

Kaz
fonte
3

Abordagem de script Python.

A idéia básica aqui é achatar todas as palavras do seu texto em uma lista e, em seguida, imprimir uma nova linha após cada segundo item (para coluna em duas colunas). Se você quiser 3 colunas, mude index%2paraindex%3

#!/usr/bin/env python3
import sys

items = [i for l in sys.stdin 
           for i in l.strip().split()]
line = []
for index,item in enumerate(items,1):
    line.append(item)
    if index%2 == 0:
       print("\t".join(line))
       line = []

Saída de amostra:

$ python recolumnate.py < input.txt                                            
a   aa
aaa b
bb  bbb
c   cc
ccc d
dd  ddd
e   ee
eee f
ff  fff
g   gg
ggg h
hh  hhh
i   ii
iii j
jj  jjj

Versão de três colunas (como dito acima, apenas index%3 == 0alterada)

$ cat recolumnate.py                                                           
#!/usr/bin/env python3
import sys

items = [i for l in sys.stdin 
           for i in l.strip().split()]
line = []
for index,item in enumerate(items,1):
    line.append(item)
    if index%3 == 0:
       print("\t".join(line))
       line = []

$ python recolumnate.py < input.txt                                            
a   aa  aaa
b   bb  bbb
c   cc  ccc
d   dd  ddd
e   ee  eee
f   ff  fff
g   gg  ggg
h   hh  hhh
i   ii  iii
j   jj  jjj
Sergiy Kolodyazhnyy
fonte