
Estou procurando uma linha única canalizável para reordenar um grande número de colunas (onde inserir manualmente os números das colunas, por exemplo, um awk
comando como awk '{print $3,$2,$1}'
não é viável). A ordem pode ser dada por um esquema de classificação (alfabética, numérica - semelhante a 'classificar', mas agindo em colunas em vez de linhas.) ou ser dada arbitrariamente em um arquivo de texto.
Responder1
Solução simples com Perl.
Primeiro preencha sua matriz de valores.
➜ ~ x="$(cat << END
22 79 83 16 25 1 4 82 34 68
48 43 2 26 39 2 71 43 57 41
77 70 73 18 76 33 21 54 67 50
6 65 46 92 25 70 53 28 3 40
32 60 76 39 26 44 34 91 24 39
59 75 96 85 52 98 69 28 72 94
48 0 88 55 6 78 1 54 83 81
3 43 48 24 23 87 28 98 38 67
97 73 74 24 92 67 1 27 90 85
32 55 52 44 26 37 87 37 100 92
END
)"
➜ ~ perl -lane '@i=sort({ @F[$a] <=> @F[$b] } 0..$#F) if $.==1;
print join("\t", @F[@i])' <<< "$x"
1 4 16 22 25 34 68 79 82 83
2 71 26 48 39 57 41 43 43 2
33 21 18 77 76 67 50 70 54 73
70 53 92 6 25 3 40 65 28 46
44 34 39 32 26 24 39 60 91 76
98 69 85 59 52 72 94 75 28 96
78 1 55 48 6 83 81 0 54 88
87 28 24 3 23 38 67 43 98 48
67 1 24 97 92 90 85 73 27 74
37 87 44 32 26 100 92 55 37 52
-a
: ativa a divisão automática que preenche automaticamente o@F
array-n
: lê cada linha em um loop while$#F
: retorna o maior índice baseado em 0 na matriz<=>
: operador de comparação para funções de classificação (somente entrada numérica, para comparações de strings usecmp
)sort
: retorna os índices classificados do array0..$#F
(usando as variáveis internas$a
e$b
)@i
: contém a matriz de índices classificados para@F
(neste exemplo,@i = 5 6 3 0 4 8 9 1 7 2
)$. == 1
: e faça isso apenas na primeira linha@F[@i]
: classifica cada linha com base nos índices classificados
Fonte:https://learnbyexample.gitbooks.io/command-line-text-processing/content/perl_the_swiss_knife.html
Responder2
Aqui está uma solução streamable.
Presumo que você queira classificar com base na primeira linha das colunas; caso contrário, adapte-se para obter a chave de classificação de outro lugar.
Gere a chave de classificação (reutilizando o array do Rush):
echo -e "b a c\n5 4 6\n8 7 9" > data
key=$(head -n1 data | sed 's/ \+/\n/g' | nl -n ln | sort -k2 | cut -f1)
$key
agora detém:
2
1
3
Agora use a chave para classificar colunas:
awk -v key="$key" '
BEGIN { split(key, order, "\n") }
{
for(i=1; i<=length(order); i++) {
printf("%s ", $order[i])
}
printf("\n");
}' data
Saída:
a b c
4 5 6
7 8 9
Responder3
Não tenho certeza se é a melhor solução e não tenho certeza se funcionará rápido em tabelas enormes, mas deve funcionar:
echo -e "2 1 3\n5 4 6\n8 7 9" | \
awk '{for (i=1;i<=NF;i++) {a[NR,i]=$i} } \
NF>p {p=NF} \
END {for (j=1;j<=p;j++) {str=a[1,j]; \
for (i=2;i<=NR;i++) {str=str" "a[i,j];}print str}}' \
| sort -n | \
awk '{for (i=1;i<=NF;i++) {a[NR,i]=$i} } \
NF>p {p=NF} \
END {for (j=1;j<=p;j++) {str=a[1,j]; \
for (i=2;i<=NR;i++) {str=str" "a[i,j];}print str}}'
Como funciona: transpõe a tabela, depois classifica a tabela e transpõe de volta.
aliás, echo -e "2 1 3\n5 4 6\n8 7 9"
resultará em
2 1 3
5 4 6
8 7 9
Após o trabalho do script, resultará em
1 2 3
4 5 6
7 8 9
obs. Acho que é possível ordenar array no awk, infelizmente não tenho tempo para fazer isso.
Responder4
Supondo que seu arquivo seja xy.dat e separado por espaço em branco:
cat xy.dat | while read line ; do
echo $line | tr ' ' '\n' | sort -nr | tr '\n' ' '
echo
done
Como meus dados de teste eram numéricos crescentes, eu uso sort -nr no coração, para torná-los decrescentes e ver algum efeito.
Agora, para torná-lo configurável, bastaria passar como parâmetros os flags para sort, o que permite -r ascendente (nenhum) e decrescente (reverso), mas também -n (numérico) e muito mais (veja:) sort --help
. Outra coisa que você pode querer configurável é o delimitador. Em branco/guia/ponto e vírgula/vírgula? Talvez um grupo regex queira "[ \t]"
significar espaço em branco ou tabulação? Mas o que usar para saída então? E você não gostaria de codificar o nome do arquivo, mas usar seu programa como filtro. Aqui está uma abordagem rápida:
#!/bin/bash
flags=$1
delim=$2
while read line ; do
echo $line | tr "$delim" '\n' | sort $flags | tr '\n' "$delim"
echo
done
invocação:
cat num.dat | bash colsort.sh "-nr" ' '
4 3 2 1
8 7 6 5
11 10 9
cat num.dat | bash colsort.sh "-r" ' '
4 3 2 1
8 7 6 5
9 11 10
cat num.dat | bash colsort.sh "--" ' '
1 2 3 4
5 6 7 8
10 11 9
Veja como é classificado por padrão com -- (alfabético: 10 11 9), reverso (9 10 11) ou numérico (11 10 9).
Principalmente como mascarar espaços em branco, tabulações e assim por diante seria útil, se documentado.