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

16

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 solução awk, mas outras soluções são bem-vindas também.

    
por Ogrish Man 08.09.2016 / 13:05

7 respostas

1

Você também pode fazer isso com uma única invocação 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 assim:

awk -f reshape.awk n=2 infile

Ou como um verso:

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

Saída:

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
    
por 09.09.2016 / 17:06
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ável:

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 - -

Por 3 colunas:

... | paste - - -

etc.

sed

Para 2 colunas:

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

Por 3 colunas:

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

etc.

xargs

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

Como xargs usa /bin/echo para imprimir, tenha em atenção que os dados que se parecem com opções para echo serã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
    
por 08.09.2016 / 15:05
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
    
por 08.09.2016 / 13:51
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ê está satisfeito com as regras padrão de divisão de palavras. Se houver alguma dúvida sobre se seus arquivos "passarão" no teste, não use essa abordagem.

Uma possibilidade seria usar printf para fazer como

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

Isso fará a divisão de palavras no conteúdo de your_file e as emparelhará e as imprimirá com guias no meio. Você pode usar mais strings %s format no printf para ter colunas extras.

    
por 08.09.2016 / 13:26
4
perl -n0E 'say s/\s+/ ++$n % 4 ?"\t":"\n"/gre' file

(substitua 4 pelo número de colunas)

    
por 09.09.2016 / 17:25
4

Utilitário BSD rs (reshape):

$ 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 0 significa "calcular linhas automaticamente a partir de colunas".

    
por 12.09.2016 / 04:54
3

Abordagem de script Python.

A ideia 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 (isso é para a coluna em duas colunas). Se você quiser 3 colunas, altere index%2 para index%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 = []

Exemplo de saída:

$ 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, somente index%3 == 0 mudou)

$ 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
    
por 09.09.2016 / 10:16