awk '{for(i=NF;i>0;i--)printf "%s ",$i;print ""}' file
Eu tenho um arquivo com colunas. Veja abaixo um exemplo:
a b c ... z
1 2 3 ... 26
Eu gostaria de trocar todas as colunas onde o primeiro se torna o último, o segundo se torna o último antes ... etc.
z y x ... a
26 25 24 ... 1
Existe um forro ( awk
ou sed
) que faz isso?
Eu sei que se pode usar awk
quando há apenas algumas colunas,
mas gostaria de poder fazer isso em arquivos com milhares de colunas.
tac
faz isso perfeitamente para linhas.
Eu acho que estou procurando o equivalente para colunas.
rev
não funcionou para mim, pois também troca conteúdo na coluna.
awk '{for(i=NF;i>0;i--)printf "%s ",$i;print ""}' file
Você pode fazer isso com um pequeno script 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))
Se você não se importar com python, então este one-liner irá reverter a ordem das colunas separadas por espaço em cada linha:
paddy$ cat infile.txt
a b c d e f g h i j k l
1 2 3 4 5 6 7 8 9 10 11 12
a e i o u
paddy$ python3 -c 'with open("infile.txt") as f: print("\n".join(" ".join(line.rstrip().split()[::-1]) for line in f))'
l k j i h g f e d c b a
12 11 10 9 8 7 6 5 4 3 2 1
u o i e a
paddy$
O acima também funciona com o python2.7:
paddy$ python2.7 -c 'with open("infile.txt") as f: print("\n".join(" ".join(line.rstrip().split()[::-1]) for line in f))'
l k j i h g f e d c b a
12 11 10 9 8 7 6 5 4 3 2 1
u o i e a
paddy$
Uma maneira de usar awk
.
Conteúdo de infile
:
a b c d e f g h i j k l
1 2 3 4 5 6 7 8 9 10 11 12
a e i o u
Execute o comando awk
a seguir:
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
Com o seguinte resultado:
l k j i h g f e d c b a
12 11 10 9 8 7 6 5 4 3 2 1
u o i e a
Isso é lento, mas tem um recurso redentor. Ele mantém a largura dos separadores de campo, quando eles são mais largos que um único caractere. FWIW: Se você executar este script duas vezes, o resultado será idêntico ao original.
Aqui está o script.
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"
Aqui estão algumas comparações de tempo. O arquivo de teste continha 1 linha.
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
Você pode usar tac
você só precisa transpor a entrada antes e depois. Isso pode ser feito com a calculadora de planilha eletrônica sc
e seu coeficiente social psc
:
< infile psc -S -r | sc -W% - | tac | psc -S -r | sc -W% - > outfile
Como visto aqui .
Isso funciona melhor quando todas as colunas estão preenchidas.
infile
a b c d e f g h i j k l
1 2 3 4 5 6 7 8 9 10 11 12
A B C D E F G H I J K L
outfile
l k j i h g f e d c b a
12 11 10 9 8 7 6 5 4 3 2 1
L K J I H G F E D C B A
Como observado por PeterO sc
tem um limite rígido de 702 colunas, então esse é o tamanho máximo suportado por este método.
Esse pipeline é mais rápido que a outra resposta mais rápida por um fator significativo (ver resultados). Usa tr
e tac
. Ele precisa utilizar 2 bytes ASCII (\ x00- \ x7F) que não existem em seus dados.
\x00
é normalmente uma boa opção, assim como \x01
, mas você pode usar qualquer byte ASCII que não esteja nos dados.
Neste exemplo, SPACE e TAB como os caracteres delimitadores. Delimitadores podem ser multi-byte ou single. O delimitador de saída é um espaço único.
Aqui está o comando. O nome do arquivo mostra o numberof fields
_x number of lines
<"$file" tr ' \t\n' '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
#!/usr/bin/awk -f
{c[$0]} END{for(i=0;i<=127;i++) {if(sprintf("%c", i) in c);else {printf "\%03o ",i}}}
' |tr -s '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
##############################################
' '\n' |tac |tr '\n' ' ' |tr '' '\n'
Se você quiser / precisar verificar os bytes não usados, verifique antecipadamente esse script opcional awk
. O tempo total, mesmo ao executar este script opcional, ainda é significativamente mais rápido do que outros métodos (até agora :). Aqui está o script de pré-processamento.
<"$file" tr ' \t\n' '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
#!/usr/bin/awk -f
{c[$0]} END{for(i=0;i<=127;i++) {if(sprintf("%c", i) in c);else {printf "\%03o ",i}}}
' |tr -s '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
##############################################
' '\n' |tac |tr '\n' ' ' |tr '' '\n'
Este é o script awk: char-ascii-not-in-stream
O segundo conjunto de vezes, para este script, inclui o tempo de char-ascii-not-in-stream
.
Você também pode fazer isso sem imprimir f :
awk 'BEGIN{ORS=""} {for(k=NF;k>0;--k) {print $k; if (k==1) print "\n"; else print " "}} ' file
Tags text-processing awk sed columns