【发布时间】:2010-10-11 17:28:56
【问题描述】:
我有一个 ksh 脚本,它返回一长串值,换行符分隔,我只想查看唯一/不同的值。可以这样做吗?
例如,假设我的输出是目录中的文件后缀:
tar gz java gz java tar class class
我想查看如下列表:
tar gz java class
【问题讨论】:
标签: bash unique distinct ksh sh
我有一个 ksh 脚本,它返回一长串值,换行符分隔,我只想查看唯一/不同的值。可以这样做吗?
例如,假设我的输出是目录中的文件后缀:
tar gz java gz java tar class class
我想查看如下列表:
tar gz java class
【问题讨论】:
标签: bash unique distinct ksh sh
您可能想查看uniq 和sort 应用程序。
(仅供参考,是的,在此命令行中排序是必需的,uniq 仅删除紧随其后的重复行)
编辑:
与Aaron Digulla 发布的关于uniq 的命令行选项的内容相反:
给定以下输入:
班级 罐 罐 罐 斌 斌 爪哇uniq 将所有行仅输出一次:
uniq -d 将输出所有出现多次的行,并打印一次:
uniq -u 将输出恰好出现一次的所有行,并将它们打印一次:
【讨论】:
sort 具有 -u 版本,用于提供唯一值。
uniq 接缝只处理相邻的行(至少默认情况下),这意味着可以在输入uniq 之前输入sort。
sort | uniq 是 95 秒 - sort -u 是 77 - @ajak6 的 awk '!a[$0]++' 是 9 秒。所以 awk 胜出,但也是最难记住的。
通过sort 和uniq 传递它们。这将删除所有重复项。
uniq -d 只给出重复的,uniq -u 只给出唯一的(去除重复)。
【讨论】:
uniq -u 不是默认行为(有关详细信息,请参阅我的答案中的编辑)
对于可能不需要排序的较大数据集,您还可以使用以下 perl 脚本:
./yourscript.ksh | perl -ne 'if (!defined $x{$_}) { print $_; $x{$_} = 1; }'
这基本上只是记住每一行输出,这样就不会再输出了。
与“sort | uniq”解决方案相比,它的优势在于无需预先进行排序。
【讨论】:
使用 zsh 你可以这样做:
% cat infile
tar
more than one word
gz
java
gz
java
tar
class
class
zsh-5.0.0[t]% print -l "${(fu)$(<infile)}"
tar
more than one word
gz
java
class
或者你可以使用 AWK:
% awk '!_[$0]++' infile
tar
more than one word
gz
java
class
【讨论】:
awk 解决方案(参见stackoverflow.com/a/21200722/45375 的解释)将适用于大文件,只要唯一行的数量足够小(因为唯一行保存在内存中)。 zsh 解决方案首先将整个文件读入内存,这对于大文件可能不是一个选项。此外,正如所写,只有没有嵌入空格的行被正确处理;要解决此问题,请改用 IFS=$'\n' read -d '' -r -A u <file; print -l ${(u)u}。
(IFS=$'\n' u=($(<infile)); print -l "${(u)u[@]}")
[@] 后缀来引用数组的所有元素 - 似乎 - 至少从版本 5 开始 - 没有它也可以工作;还是您只是为了清楚起见才添加它?
print -l "${(fu)$(<infile)}"
awk 示例输出。
./script.sh | sort -u
这与monoxide'sanswer 相同,但更简洁一些。
【讨论】:
... | sort | uniq效率更高,因为是一次性完成的
sort -u 不需要在每次找到已经遇到的较早值时更新排序列表。而 sort | 必须在将 all 项传递给 uniq 之前对其进行排序
sort -u 可能会更快,因为任何最佳比较排序算法都具有O(n*log(n)) 复杂度,但是可以找到具有O(n) 复杂度的所有唯一值使用 Hash Set 数据结构。尽管如此,sort -u 和sort | uniq 的性能几乎相同,而且它们都很慢。我对我的系统进行了一些测试,更多信息请访问gist.github.com/sda97ghb/690c227eb9a6b7fb9047913bfe0e431d
根据要求是唯一的(但未排序);
使用少于约 70 个元素的系统资源(经时间测试);
编写为从标准输入获取输入,
(或修改并包含在另一个脚本中):
(重击)
bag2set () {
# Reduce a_bag to a_set.
local -i i j n=${#a_bag[@]}
for ((i=0; i < n; i++)); do
if [[ -n ${a_bag[i]} ]]; then
a_set[i]=${a_bag[i]}
a_bag[i]=$'\0'
for ((j=i+1; j < n; j++)); do
[[ ${a_set[i]} == ${a_bag[j]} ]] && a_bag[j]=$'\0'
done
fi
done
}
declare -a a_bag=() a_set=()
stdin="$(</dev/stdin)"
declare -i i=0
for e in $stdin; do
a_bag[i]=$e
i=$i+1
done
bag2set
echo "${a_set[@]}"
【讨论】:
使用 AWK,您可以:
./yourscript.ksh | awk '!a[$0]++'
我发现它比 sort 和 uniq 更快
【讨论】:
我得到了一个更好的提示来获取文件中的非重复条目
awk '$0 != x ":FOO" && NR>1 {print x} {x=$0} END {print}' file_name | uniq -f1 -u
【讨论】: