¿Cómo procesar un archivo de texto de varias columnas para obtener otro archivo de texto de varias columnas?

17

Tengo un archivo 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

¿Cómo puedo procesarlo y obtener un archivo de 2 columnas 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

O un archivo de tres columnas 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

Prefiero obtener una solución awk, pero otras soluciones también son bienvenidas.

Solo un alumno
fuente

Respuestas:

1

También puede hacerlo con una sola invocación de 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"
}

Ejecútelo así:

awk -f reshape.awk n=2 infile

O como una frase:

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

Salida:

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

O con 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
fuente
¿No se usa esto $1como la cadena de formatoprintf ?
Comodín el
@Wildcard: Correcto, es más seguro de usar "%s", .... Actualizado
Thor
Gracias por confirmar. :) Lo mismo se aplica al awkcomando en su otra respuesta a esta pregunta, por cierto.
Comodín el
20

Ponga cada campo en una línea y post-columnate.

Cada campo en una línea

tr

tr -s ' ' '\n' < infile

grep

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

sed

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

o más portátil:

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

awk

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

o

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

Columnate

pegar

Para 2 columnas:

... | paste - -

Para 3 columnas:

... | paste - - -

etc.

sed

Para 2 columnas:

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

Para 3 columnas:

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

etc.

xargs

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

Como se xargsusa /bin/echopara imprimir, tenga en cuenta que los datos que parecen opciones echoserán interpretados como tales.

awk

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

pr

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

o

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

columnas (del paquete autogen)

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

Salida 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
fuente
2
Slam dunk. +1 señor
Steven Penny
¿No debería xargsllamar la línea echoo printf?
Comodín el
1
@Wildcard: xargsllamadas /bin/echopor defecto
Thor
1
Wow, no tenía idea! Incluso lo especifica POSIX . ¡Gracias!
Comodín el
@Wildcard: el envío de datos xargsparece opciones para /bin/echocausar problemas ... Agregué una advertencia.
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
fuente
9

Como señaló Wildcard, esto solo funcionará si su archivo está bien formateado, ya que no hay caracteres especiales que el intérprete interpretará como globos y usted está satisfecho con las reglas predeterminadas de división de palabras. Si hay alguna pregunta sobre si sus archivos "pasarán" esa prueba, no utilice este enfoque.

Una posibilidad sería usarlo printfpara hacerlo como

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

Eso dividirá las palabras en el contenido your_filey las emparejará e imprimirá con pestañas intermedias. Podría usar más %scadenas de formato printfpara tener columnas adicionales.

Eric Renouf
fuente
1
Depende del archivo que no contenga caracteres especiales. Si tiene, por ejemplo, algún asterisco (*) obtendrá resultados muy inesperados.
Comodín el
4
perl -n0E 'say s/\s+/ ++$n % 4 ?"\t":"\n"/gre' file

(reemplace 4 por el número de columnas)

JJoao
fuente
4

rsUtilidad BSD (remodelación):

$ 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 2es filas y columnas . Especificar 0significa "calcular filas automáticamente a partir de columnas".

Kaz
fuente
3

Enfoque de script Python.

La idea básica aquí es aplanar todas las palabras en su texto en una lista, y luego imprimir una nueva línea después de cada segundo elemento (es decir, en columnas en dos columnas). Si desea 3 columnas, cambie index%2aindex%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 = []

Salida de muestra:

$ 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

Versión de tres columnas (como se dijo anteriormente, solo index%3 == 0cambió)

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