![如何对大量列进行重新排序?](https://linux22.com/image/22479/%E5%A6%82%E4%BD%95%E5%AF%B9%E5%A4%A7%E9%87%8F%E5%88%97%E8%BF%9B%E8%A1%8C%E9%87%8D%E6%96%B0%E6%8E%92%E5%BA%8F%EF%BC%9F.png)
我正在寻找一种可管道化的单行代码来对大量列进行重新排序(其中手动输入列号,例如在awk
诸如此类的命令中awk '{print $3,$2,$1}'
是不可行的)。顺序可以通过排序方案给出(字母、数字 - 就像“排序”,但作用于列而不是行。)或者在文本文件中任意给出。
答案1
使用 Perl 的简单解决方案。
首先填充您的值数组。
➜ ~ 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
:启用自动拆分,自动填充@F
数组-n
:在 while 循环中读取每一行$#F
:返回数组中最大的从 0 开始的索引<=>
:排序函数的比较运算符(仅限数字输入,用于字符串比较时使用cmp
)sort
:从数组返回排序后的索引0..$#F
(使用内置$a
和$b
变量)@i
@F
:包含(在本例中,@i = 5 6 3 0 4 8 9 1 7 2
)的排序索引数组$. == 1
: 并且只在第一行执行@F[@i]
:根据排序后的索引对每一行进行排序
来源:https://learnbyexample.gitbooks.io/command-line-text-processing/content/perl_the_swiss_knife.html
答案2
这是一个可流式传输的解决方案。
我假设您想根据列的第一行进行排序,否则适应从其他地方获取排序键。
生成排序键(重用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
现在成立:
2
1
3
现在使用键对列进行排序:
awk -v key="$key" '
BEGIN { split(key, order, "\n") }
{
for(i=1; i<=length(order); i++) {
printf("%s ", $order[i])
}
printf("\n");
}' data
输出:
a b c
4 5 6
7 8 9
答案3
我不确定它是最好的解决方案,也不确定它能否在巨大的桌子上快速运行,但它应该可以:
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}}'
它是如何工作的:它转置表格,然后对表格进行排序并将其转置回去。
顺便说一句echo -e "2 1 3\n5 4 6\n8 7 9"
将导致
2 1 3
5 4 6
8 7 9
脚本工作后,结果将是
1 2 3
4 5 6
7 8 9
附注我认为可以在 awk 中对数组进行排序,不幸的是我没有足够的时间来做到这一点。
答案4
假设您的文件是 xy.dat,并以空格分隔:
cat xy.dat | while read line ; do
echo $line | tr ' ' '\n' | sort -nr | tr '\n' ' '
echo
done
由于我的测试数据是数字升序,所以我在心中使用 sort -nr ,使其降序,并看到一些效果。
现在,为了使其可配置,只需将排序标志作为参数传递,它允许升序(无)和降序 -r (反向),还可以 -n (数字)等等(请参阅:sort --help
)。您可能喜欢配置的另一件事是分隔符。空白/制表符/分号/逗号?也许正则表达式组喜欢"[ \t]"
表示空白或制表符?那么输出用什么呢?您不想对文件名进行硬编码,而是使用您的程序作为过滤器。这是一个快速方法:
#!/bin/bash
flags=$1
delim=$2
while read line ; do
echo $line | tr "$delim" '\n' | sort $flags | tr '\n' "$delim"
echo
done
调用:
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
查看默认情况下如何排序:--(按字母顺序:10 11 9)、反向(9 10 11)或数字(11 10 9)。
如果有记录的话,主要是如何屏蔽空白、制表符等会很有帮助。