Intercambio de un número ilimitado de columnas

Tengo un file con columnas. Vea abajo para un ejemplo:

abc ... z 1 2 3 ... 26 

Me gustaría cambiar todas las columnas donde la primera se convierte en la última, la segunda se convierte en la última antes … etc.

 zyx ... a 26 25 24 ... 1 

¿Hay un trazador de líneas ( awk o sed ) que hace esto?
Sé que se puede usar awk cuando solo hay un par de columnas, pero me gustaría poder hacerlo en files con miles de columnas.

tac hace esto perfectamente para líneas.
Creo que estoy buscando el equivalente para las columnas.

rev no me ha funcionado, ya que también intercambia contenido en la columna.

 awk '{for(i=NF;i>0;i--)printf "%s ",$i;print ""}' file 

Podrías hacerlo con un pequeño script de python:

 #!/usr/bin/env python # Swaps order of columns in file, writes result to a file. # usage: program.py input_file output_file import sys, os out = [] for line in open(sys.argv[1], 'r'): fields = line.split() rev = ' '.join(list(reversed(fields))) out.append(rev) f = open(sys.argv[2], 'w') f.write(os.linesep.join(out)) 

Si no te importa Python, este one-liner invertirá el order de las columnas separadas por espacios en cada línea:

 paddy$ cat infile.txt abcdefghijkl 1 2 3 4 5 6 7 8 9 10 11 12 aeiou paddy$ python3 -c 'with open("infile.txt") as f: print("\n".join(" ".join(line.rstrip().split()[::-1]) for line in f))' lkjihgfedcba 12 11 10 9 8 7 6 5 4 3 2 1 uoiea paddy$ 

Lo anterior también funciona con python2.7:

 paddy$ python2.7 -c 'with open("infile.txt") as f: print("\n".join(" ".join(line.rstrip().split()[::-1]) for line in f))' lkjihgfedcba 12 11 10 9 8 7 6 5 4 3 2 1 uoiea paddy$ 

Una forma de usar awk .

Contenido de infile :

 abcdefghijkl 1 2 3 4 5 6 7 8 9 10 11 12 aeiou 

Ejecutar siguiendo el command awk :

 awk '{ ## Variable 'i' will be incremented from first field, variable 'j' ## will be decremented from last field. And their values will be exchanged. ## The loop will end when both values cross themselves. j = NF; for ( i = 1; i <= NF; i++ ) { if ( j - i < 1 ) { break; } temp = $j; $j = $i; $i = temp; j--; } print; }' infile 

Con el siguiente resultado:

 lkjihgfedcba 12 11 10 9 8 7 6 5 4 3 2 1 uoiea 

Esto es lento, pero tiene una function de canje. Mantiene el ancho de los separadores de campo, cuando son más anchos que un solo carácter. FWIW: si ejecuta este script dos veces, el resultado es idéntico al original.

Aquí está el guión.

 awk '{ eix = length($0) for( fn=NF; fn>0; fn--) { dix=eix while( substr($0,dix,1) ~ /[ \t]/ ) dix-- printf "%s%s", substr($0,dix+1,eix-dix), $fn dix-=length($fn); eix=dix } print substr($0,1,dix) }' "$file" 

Aquí hay algunas comparaciones de time. El file de testing contenía 1 línea.

  fields fields 10,0000 10,000,000 user11136 {python} | real 0.029s real 3.235s reversible? no | user 0.032s user 2.008s | sys 0.000s sys 1.228s jmp {python} | real 0.078s real 5.045s reversible? no | user 0.068s user 4.268s | sys 0.012s sys 0.560s rush {awk} | real 0.120s real 10.889s reversible? no | user 0.116s user 8.641s | sys 0.008s sys 2.252s petero {awk} | real 0.319s real 35.750s reversible? yes | user 0.304s user 33.090s | sys 0.016s sys 2.660s 

Puede usar tac solo tiene que transponer la input antes y después. Esto se puede hacer con la calculadora sc spreadsheet y su comstackdor psc :

 < infile psc -S -r | sc -W% - | tac | psc -S -r | sc -W% - > outfile 

Como se ve aquí .

Esto funciona mejor cuando todas las columnas están llenas.

en file

  abcdefghijkl 1 2 3 4 5 6 7 8 9 10 11 12 ABCDEFGHIJKL 

file de salida

  lkjihgfedcba 12 11 10 9 8 7 6 5 4 3 2 1 LKJIHGFEDCBA 

Editar

Como notó PeterO, sc tiene un límite estricto de 702 columnas, por lo que ese es el tamaño máximo admitido por este método.

Esta tubería es más rápida que la otra respuesta más rápida por un factor significativo (ver resultados). Utiliza tr y tac . Necesita utilizar 2 bytes ASCII (\ x00- \ x7F) que no existen en sus datos.

\x00 suele ser una buena opción, como lo es \x01 , pero puede usar cualquier byte ASCII que no esté en los datos.

En este ejemplo, SPACE y TAB como los caracteres delimitadores. Los delimitadores pueden ser multibyte o simples. El delimitador de salida es un espacio único.

Aquí está el command. El nombre del file muestra el numberof fields _x number of lines

  <"$file" tr ' \t\n' '\0\0\1' |tr -s '\0' '\n' |tac |tr '\n' ' ' |tr '\1' '\n' 

Si desea / necesita verificar los bytes no utilizados, puede verificar de antemano con este script awk opcional. El time total, incluso cuando se ejecuta este script opcional, es todavía significativamente más rápido que otros methods (hasta ahora 🙂 … Aquí está el script de procesamiento previo.

 o=($(<"$file" char-ascii-not-in-stream)); x="${o[0]}"; y="${o[1]}" <"$file" tr ' \t\n' "$x$x$y" |tr -s "$x" '\n' |tac |tr '\n' ' ' | tr '$y' '\n' >"$file".$user 

Este es el script de awk: char-ascii-not-in-stream

 #!/usr/bin/awk -f {c[$0]} END{for(i=0;i<=127;i++) {if(sprintf("%c", i) in c);else {printf "\\%03o ",i}}} 

El segundo set de veces, para este script, incluye el time de char-ascii-not-in-stream .

 Peter.O {tr,tac,tr} ==== file_10_x10000 real 0m0.013s 0m0.015s user 0m0.020s 0m0.020s sys 0m0.008s 0m0.012s user11136 {python} ===== file_10_x10000 real 0m0.057s user 0m0.048s sys 0m0.008s jmp {python} =========== file_10_x10000 real 0m0.160s user 0m0.160s sys 0m0.000s rush {awk} ============= file_10_x10000 real 0m0.121s user 0m0.120s sys 0m0.000s ############################################## Peter.O {tr,tac,tr} ==== file_1000_x1000 real 0m0.048s 0m0.059s user 0m0.040s 0m0.040s sys 0m0.040s 0m0.048s user11136 {python} ===== file_1000_x1000 real 0m0.158s user 0m0.136s sys 0m0.028s jmp {python} =========== file_1000_x1000 real 0m0.327s user 0m0.320s sys 0m0.008s rush {awk} ============= file_1000_x1000 real 0m0.832s user 0m0.820s sys 0m0s012s ############################################## Peter.O {tr,tac,tr} ==== file_1000000_x50 real 0m5.221s 0m6.458s user 0m4.208s 0m5.248s sys 0m2.624s 0m2.396s user11136 {python} ===== file_1000000_x50 real 0m16.286s user 0m10.041s sys 0m5.148s jmp {python} =========== file_1000000_x50 real 0m22.845s user 0m20.705s sys 0m1.140s rush {awk} ============= file_1000000_x50 real 0m44.793s user 0m43.583s sys 0m0.848s ############################################## 

También puede hacerlo sin imprimir f :

 awk 'BEGIN{ORS=""} {for(k=NF;k>0;--k) {print $k; if (k==1) print "\n"; else print " "}} ' file