Respostas:
Para a cut(1)
página do manual:
Use um e apenas um de -b, -c ou -f. Cada lista é composta de um intervalo, ou muitos intervalos separados por vírgulas. A entrada selecionada é gravada na mesma ordem em que é lida e é gravada exatamente uma vez.
Ele atinge o campo 1 primeiro, para que seja impresso, seguido pelo campo 2.
Use em awk
vez disso:
awk '{ print $2 " " $1}' file.txt
FS
é uma opção, OFS
é uma variável. por exemploawk -v OFS=";" -F"\t" '{print $2,$1}'
| sed 's/\r//' |
antes de tubulação paraawk
awk '{print $4 "\t" $2 "\t" $6 "\t" $7}' file
Você também pode combinar cut
e paste
:
paste <(cut -f2 file.txt) <(cut -f1 file.txt)
via comentários: é possível evitar bashisms e remover uma instância de corte fazendo:
paste file.txt file.txt | cut -f2,3
cut
funciona bem para colunas de comprimento variável, desde que você tenha um separador de colunas exclusivo.
bash
ismos e remover uma instância do seguinte modo cut
: paste file.txt file.txt | cut -f2,3
usando apenas a concha,
while read -r col1 col2
do
echo $col2 $col1
done <"file"
"$col2"
e "$col1"
- pode haver metacaracteres de shell ou outras travessuras nos dados.
Você pode usar o Perl para isso:
perl -ane 'print "$F[1] $F[0]\n"' < file.txt
A vantagem de executar o perl é que (se você conhece o Perl), é possível fazer muito mais computação em F do que reorganizar as colunas.
perl -ae print
funciona como cat
para mim
Usando join
:
join -t $'\t' -o 1.2,1.1 file.txt file.txt
Notas:
-t $'\t'
Em GNU join
o mais intuitivo -t '\t'
, sem a $
falhar, ( coreutils v8.28 e anteriores?); provavelmente é um bug que uma solução alternativa $
deve ser necessária. Veja: unix join separator char .
join
precisa de dois nomes de arquivos, mesmo que apenas um arquivo esteja sendo trabalhado. Usar o mesmo nome duas vezes engana join
para executar a ação desejada.
Para sistemas com poucos recursos, join
oferece uma área útil menor do que algumas das ferramentas usadas em outras respostas:
wc -c $(realpath `which cut join sed awk perl`) | head -n -1
43224 /usr/bin/cut
47320 /usr/bin/join
109840 /bin/sed
658072 /usr/bin/gawk
2093624 /usr/bin/perl
Acabei de trabalhar em algo muito semelhante, não sou especialista, mas pensei em compartilhar os comandos que usei. Eu tinha um csv de várias colunas que exigia apenas 4 colunas e, em seguida, precisava reordená-las.
Meu arquivo era o canal '|' delimitado, mas que pode ser trocado.
LC_ALL=C cut -d$'|' -f1,2,3,8,10 ./file/location.txt | sed -E "s/(.*)\|(.*)\|(.*)\|(.*)\|(.*)/\3\|\5\|\1\|\2\|\4/" > ./newcsv.csv
É certo que é realmente áspero e pronto, mas pode ser ajustado para se adequar!
Usando sed
Use sed com subexpressões aninhadas da expressão regular básica para capturar e reordenar o conteúdo da coluna. Essa abordagem é mais adequada quando há um número limitado de cortes para reordenar colunas, como neste caso.
A idéia básica é cercar partes interessantes do padrão de pesquisa com \(
e \)
, que podem ser reproduzidas no padrão de substituição com \#
where #
representa a posição seqüencial da subexpressão no padrão de pesquisa.
Por exemplo:
$ echo "foo bar" | sed "s/\(foo\) \(bar\)/\2 \1/"
rendimentos:
bar foo
O texto fora de uma subexpressão é digitalizado, mas não retido para reprodução na sequência de substituição.
Embora a questão não tenha discutido colunas de largura fixa, discutiremos aqui, pois essa é uma medida digna de qualquer solução apresentada. Para simplificar, vamos assumir que o arquivo é delimitado por espaço, embora a solução possa ser estendida para outros delimitadores.
Espaços em Colapso
Para ilustrar o uso mais simples, vamos supor que vários espaços possam ser recolhidos em espaços únicos e os valores da segunda coluna são finalizados com EOL (e não preenchido com espaço).
Arquivo:
bash-3.2$ cat f
Column1 Column2
str1 1
str2 2
str3 3
bash-3.2$ od -a f
0000000 C o l u m n 1 sp sp sp sp C o l u m
0000020 n 2 nl s t r 1 sp sp sp sp sp sp sp 1 nl
0000040 s t r 2 sp sp sp sp sp sp sp 2 nl s t r
0000060 3 sp sp sp sp sp sp sp 3 nl
0000072
Transformar:
bash-3.2$ sed "s/\([^ ]*\)[ ]*\([^ ]*\)[ ]*/\2 \1/" f
Column2 Column1
1 str1
2 str2
3 str3
bash-3.2$ sed "s/\([^ ]*\)[ ]*\([^ ]*\)[ ]*/\2 \1/" f | od -a
0000000 C o l u m n 2 sp C o l u m n 1 nl
0000020 1 sp s t r 1 nl 2 sp s t r 2 nl 3 sp
0000040 s t r 3 nl
0000045
Preservando as larguras das colunas
Vamos agora estender o método para um arquivo com colunas de largura constante, permitindo que as colunas tenham larguras diferentes.
Arquivo:
bash-3.2$ cat f2
Column1 Column2
str1 1
str2 2
str3 3
bash-3.2$ od -a f2
0000000 C o l u m n 1 sp sp sp sp C o l u m
0000020 n 2 nl s t r 1 sp sp sp sp sp sp sp 1 sp
0000040 sp sp sp sp sp nl s t r 2 sp sp sp sp sp sp
0000060 sp 2 sp sp sp sp sp sp nl s t r 3 sp sp sp
0000100 sp sp sp sp 3 sp sp sp sp sp sp nl
0000114
Transformar:
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f2
Column2 Column1
1 str1
2 str2
3 str3
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f2 | od -a
0000000 C o l u m n 2 sp C o l u m n 1 sp
0000020 sp sp nl 1 sp sp sp sp sp sp sp s t r 1 sp
0000040 sp sp sp sp sp nl 2 sp sp sp sp sp sp sp s t
0000060 r 2 sp sp sp sp sp sp nl 3 sp sp sp sp sp sp
0000100 sp s t r 3 sp sp sp sp sp sp nl
0000114
Por fim, embora o exemplo da pergunta não tenha seqüências de comprimento desigual, essa expressão sed suporta esse caso.
Arquivo:
bash-3.2$ cat f3
Column1 Column2
str1 1
string2 2
str3 3
Transformar:
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f3
Column2 Column1
1 str1
2 string2
3 str3
bash-3.2$ sed "s/\([^ ]*\)\([ ]*\) \([^ ]*\)\([ ]*\)/\3\4 \1\2/" f3 | od -a
0000000 C o l u m n 2 sp C o l u m n 1 sp
0000020 sp sp nl 1 sp sp sp sp sp sp sp s t r 1 sp
0000040 sp sp sp sp sp nl 2 sp sp sp sp sp sp sp s t
0000060 r i n g 2 sp sp sp nl 3 sp sp sp sp sp sp
0000100 sp s t r 3 sp sp sp sp sp sp nl
0000114
Comparação com outros métodos de reordenação de colunas no shell
Surpreendentemente, para uma ferramenta de manipulação de arquivos, o awk não é adequado para cortar de um campo para o final do registro. No sed, isso pode ser realizado usando expressões regulares, por exemplo, \(xxx.*$\)
onde xxx
está a expressão que corresponde à coluna.
O uso de subshells de colar e cortar fica complicado ao implementar scripts shell internos. O código que funciona a partir da linha de comando falha ao analisar quando trazido para dentro de um script de shell. Pelo menos essa foi a minha experiência (o que me levou a essa abordagem).
Expandindo a resposta do @Met, também usando Perl:
se a entrada e a saída forem delimitadas por TAB:
perl -F'\t' -lane 'print join "\t", @F[1, 0]' in_file
Se a entrada e a saída forem delimitadas por espaço em branco:
perl -lane 'print join " ", @F[1, 0]' in_file
Aqui,
-e
diz ao Perl para procurar o código embutido, em vez de em um arquivo de script separado,
-n
lê a linha de entrada 1 por vez,
-l
remove o separador de registros de entrada ( \n
no * NIX) após ler a linha (semelhante a chomp
) e adiciona a saída separador de registros ( \n
no * NIX) para cada um print
,
-a
divide a linha de entrada no espaço em branco na matriz @F
,
-F'\t'
em combinação com -a
divide a linha de entrada nas TABs, em vez de espaço em branco na matriz @F
.
@F[1, 0]
é a matriz composta pelos segundo e primeiro elementos da matriz @F
, nesta ordem. Lembre-se de que as matrizes em Perl são indexadas a zero, enquanto os campos em cut
são indexadas a 1. Portanto, os campos em @F[0, 1]
são os mesmos que os campos cut -f1,2
.
Observe que essa notação permite uma manipulação mais flexível da entrada do que em algumas outras respostas postadas acima (que são boas para uma tarefa simples). Por exemplo:
# reverses the order of fields:
perl -F'\t' -lane 'print join "\t", reverse @F' in_file
# prints last and first fields only:
perl -F'\t' -lane 'print join "\t", @F[-1, 0]' in_file
cut
que não suporta este comando de reordenamento intuitivo. De qualquer forma, outra dica: você pode usarawk
's'-FS
e-OFS
opções para usar separadores de campos de entrada e saída personalizados (como-d
e--output-delimiter
paracut
).