按 bash 中的行数对文本列进行排序
Sort text columns by number of lines in bash
假设一个文本文件包含 x 个字符串列。
$cat file # where x=3
foo foo foo
bar bar bar
baz baz
qux
bash 中有没有一种方法可以根据这些列包含的数字文本字符串(即填充的行)对这些列进行排序,同时保持行的内部顺序在每列中?
$sought_command file
foo foo foo
bar bar bar
baz baz
qux
本质上,行数最多的列排在第一位,行数第二多的列排在第二位,依此类推
(这个任务很容易通过 R
实现,但我想知道通过 bash 的解决方案。)
编辑 1:
这里有一些额外的细节:每一列至少包含一个文本字符串(即,一个填充行)。文本字符串可以构成任何字母数字组合并具有任何长度(但显然不包含空格)。输出列不得插入空行。列分隔符没有先验限制,只要它在 table.
中保持一致即可
此任务所需要做的就是按原样移动列,以便它们按列长度排序。 (我知道在 bash 中实现这个听起来比实际更容易。)
首先像这样创建一个名为 transpose 的函数:
transpose() {
awk -v FPAT='[^[:blank:]]+|[ \t]{3,}' '{
for (i=1; i<=NF; i++)
a[i,NR]=$i
max=(max<NF?NF:max)
}
END {for (i=1; i<=max; i++)
for (j=1; j<=NR; j++)
printf "%s%s", a[i,j], (j==NR?ORS:OFS)
}'
}
然后将其用作:
transpose < file | awk '{print NF "\t" [=11=]}' | sort -k1nr | cut -f2- | transpose
foo foo foo
bar bar bar
baz baz
qux
步骤是:
- 调用
transpose
函数将列转置为行
- 使用
awk
在每行的开头添加# of fields
- 在第一列中按相反的数字顺序使用
sort
- 使用
cut
删除第一列
- 再次调用
transpose
将列转置为行以获得原始顺序
PS: 由于使用 FPAT
我们这里需要 gnu-awk。
sed -e 's/^ *//' columns.txt
# =>
# foo foo foo
# bar bar bar
# baz baz
# qux
我整个星期都会在这里! :D
更严肃地说,您可能想要 transpose your columns with bash,使用 awk
或 rs
。这将使您更容易对列(现在是行)进行排序,然后再将它们转回原来的位置。
不过,多个空格可能会给 awk
带来问题。
使用 unix 工具集
$ tr '\t' '\n' <file |
pr -4ts |
awk '{print gsub(/-/,"-") "\t" [=10=]}' |
sort -k1n |
cut -f2- |
tr '\t' '\n' |
pr -3ts
foo foo foo
bar bar bar
baz baz -
qux - -
假设列以制表符分隔,缺失值用“-”表示。幻数4和3分别是行数和列数。
将其用作输入文件
$ cat file
foo foo foo
bar bar bar
- baz baz
- qux -
使用 GNU awk for sorted_in 并假设您的列是制表符分隔的:
$ cat tst.awk
BEGIN{ FS=OFS="\t" }
{
for (i=1; i<=NF; i++) {
if ($i ~ /[^[:space:]]/) {
cell[NR,i] = $i
cnt[i]++
}
}
next
}
END {
PROCINFO["sorted_in"] = "@val_num_desc"
for (row=1; row<=NR; row++) {
c=0
for (col in cnt) {
printf "%s%s", (c++?OFS:""), cell[row,col]
}
print ""
}
}
$ awk -f tst.awk file
foo foo foo
bar bar bar
baz baz
qux
假设一个文本文件包含 x 个字符串列。
$cat file # where x=3
foo foo foo
bar bar bar
baz baz
qux
bash 中有没有一种方法可以根据这些列包含的数字文本字符串(即填充的行)对这些列进行排序,同时保持行的内部顺序在每列中?
$sought_command file
foo foo foo
bar bar bar
baz baz
qux
本质上,行数最多的列排在第一位,行数第二多的列排在第二位,依此类推
(这个任务很容易通过 R
实现,但我想知道通过 bash 的解决方案。)
编辑 1:
这里有一些额外的细节:每一列至少包含一个文本字符串(即,一个填充行)。文本字符串可以构成任何字母数字组合并具有任何长度(但显然不包含空格)。输出列不得插入空行。列分隔符没有先验限制,只要它在 table.
中保持一致即可此任务所需要做的就是按原样移动列,以便它们按列长度排序。 (我知道在 bash 中实现这个听起来比实际更容易。)
首先像这样创建一个名为 transpose 的函数:
transpose() {
awk -v FPAT='[^[:blank:]]+|[ \t]{3,}' '{
for (i=1; i<=NF; i++)
a[i,NR]=$i
max=(max<NF?NF:max)
}
END {for (i=1; i<=max; i++)
for (j=1; j<=NR; j++)
printf "%s%s", a[i,j], (j==NR?ORS:OFS)
}'
}
然后将其用作:
transpose < file | awk '{print NF "\t" [=11=]}' | sort -k1nr | cut -f2- | transpose
foo foo foo
bar bar bar
baz baz
qux
步骤是:
- 调用
transpose
函数将列转置为行 - 使用
awk
在每行的开头添加# of fields - 在第一列中按相反的数字顺序使用
sort
- 使用
cut
删除第一列 - 再次调用
transpose
将列转置为行以获得原始顺序
PS: 由于使用 FPAT
我们这里需要 gnu-awk。
sed -e 's/^ *//' columns.txt
# =>
# foo foo foo
# bar bar bar
# baz baz
# qux
我整个星期都会在这里! :D
更严肃地说,您可能想要 transpose your columns with bash,使用 awk
或 rs
。这将使您更容易对列(现在是行)进行排序,然后再将它们转回原来的位置。
不过,多个空格可能会给 awk
带来问题。
使用 unix 工具集
$ tr '\t' '\n' <file |
pr -4ts |
awk '{print gsub(/-/,"-") "\t" [=10=]}' |
sort -k1n |
cut -f2- |
tr '\t' '\n' |
pr -3ts
foo foo foo
bar bar bar
baz baz -
qux - -
假设列以制表符分隔,缺失值用“-”表示。幻数4和3分别是行数和列数。
将其用作输入文件
$ cat file
foo foo foo
bar bar bar
- baz baz
- qux -
使用 GNU awk for sorted_in 并假设您的列是制表符分隔的:
$ cat tst.awk
BEGIN{ FS=OFS="\t" }
{
for (i=1; i<=NF; i++) {
if ($i ~ /[^[:space:]]/) {
cell[NR,i] = $i
cnt[i]++
}
}
next
}
END {
PROCINFO["sorted_in"] = "@val_num_desc"
for (row=1; row<=NR; row++) {
c=0
for (col in cnt) {
printf "%s%s", (c++?OFS:""), cell[row,col]
}
print ""
}
}
$ awk -f tst.awk file
foo foo foo
bar bar bar
baz baz
qux