我有一个清单如下:
1 2 5 2
1 5 5 3
1 5 5 5
5 2 2 2
2 2 4 3
我想对每一行进行排序并获取如下的唯一值,类似于sort | uniq
:
1 2 5
1 3 5
1 5
2 5
2 3 4
我一直在网上寻找解决方案,但只能找到按列排序的解决方案。我怎样才能得到输出?提前致谢。
答案1
由于对行进行排序比对行中的列进行排序更容易,因此一种方法可以是转置每一行(以便每个字段变成一行),应用sort
然后uniq
转置它们。
这是一个简单的实现,假设使用 GNU 工具:
$ while read -r line; do echo "$line" | grep -o '[^ ]*' | sort -h | uniq | paste -s; done <file
file
对于每一行,它循环遍历and :
grep
使用-o
选项(仅打印每行的匹配部分)将其输入拆分为n行,每个匹配子字符串一行。在这里,我们匹配除空格之外的所有内容。- 分割线使用
-h
比较人类可读数字的选项进行排序(如果您想将字段按字母数字字符串排序,请删除-h
)。 - 该
uniq
命令删除重复项。 paste -s
将标准输入中的每一行打印为由制表符分隔的单行字段。您可以附加一个结尾| tr '\t' ' '
将制表符更改为空格。
但请注意,使用循环来处理文本通常是被认为是不好的做法。
答案2
以下不会跨列对数据进行排序,只是提取唯一值。目前还不清楚是否需要排序。
使用awk
:
$ awk '{ n=split($0,a,FS); $0=""; j=1; delete u; for (i=1; i<=n; i++) if (!u[a[i]]++) $(j++) = a[i]; print }' <file
1 2 5
1 5 3
1 5
5 2
2 4 3
该程序布局很好,带有注释:
{
# split the current record into fields in the array a
n = split($0, a, FS)
# empty the current record
$0=""
# j is the next field number that we are to set
# in the record that we are building
j=1
# seen is an associative array that we use to
# keep track of whether we've seen a bit of
# data before from this record
delete seen
# loop over the entries in a (the original
# fields of the input data)
for (i=1; i<=n; i++)
# if we haven't seen this data before,
# mark it as seen and...
if (!seen[a[i]]++)
# add it to the j:th field in the new record
$(j++) = a[i]
print
}
我在这里的想法是为每行输入构建一个输出记录,其中包含原始数据中的唯一字段。
默认情况下,“记录”与“行”同义,“字段”与“列”同义(这些只是更通用的词,取决于 和 中的当前值RS
)FS
。
答案3
使用 Perl:
perl -MList::Util=uniq -alne 'print join " ", sort { $a <=> $b } uniq @F' file
1 2 5
1 3 5
1 5
2 5
2 3 4
答案4
另一种bash
方法,类似于@fra-san 的方法。
while read X;do tr<<<$X ' ' \\n|sort -u|paste -sd" ";done<file
1 2 5
1 3 5
1 5
2 5
2 3 4