从UNIX Shell脚本中的列表中选择唯一或不同的值


238

我有一个ksh脚本,该脚本返回一长串值,用换行符分隔,并且我只想查看唯一/不同的值。有可能这样做吗?

例如,说我的输出是目录中的文件后缀:

tar
gz
java
gz
java
tar
class
class

我想查看类似的列表:

tar
gz
java
class

Answers:


432

您可能需要查看uniqsort应用程序。

./yourscript.ksh | 排序 优衣库

(仅供参考,是的,在此命令行中有必要进行排序,uniq只删除彼此紧接的重复行)

编辑:

亚伦·迪古拉Aaron Digulla)关于uniq的命令行选项发布的内容相反:

给出以下输入:

类
罐
罐
罐
箱子
箱子
爪哇

uniq 将只输出一次所有行:

类
罐
箱子
爪哇

uniq -d 将输出所有出现多次的行,并且将它们打印一次:

罐
箱子

uniq -u 将输出所有仅出现一次的行,并将其打印一次:

类
爪哇

2
只是供后来者参考:@AaronDigulla的答案此后已得到纠正。
mklement0 2014年

2
非常好一点,这种“排序在此命令行中是必需的,uniq仅剥离我刚刚学到的紧接彼此的重复行!”
HattrickNZ 2015年

4
GNU sort还提供了一个-u用于提供唯一值的版本。
Arthur2e5

我发现uniq接缝只能处理相邻的线(至少默认情况下),这意味着sort在送入之前可以输入uniq
Stphane '16


10

对于可能不希望进行排序的较大数据集,您还可以使用以下perl脚本:

./yourscript.ksh | perl -ne 'if (!defined $x{$_}) { print $_; $x{$_} = 1; }'

基本上,这只是记住每条线的输出,因此不会再次输出。

与“ sort | uniq”解决方案相比,它的优势在于无需预先排序。


2
请注意,对很大的文件进行排序本身并不是排序的问题。它可以对大于可用RAM +交换的文件进行排序。如果只有少量重复项,则OTOH Perl将失败。
亚伦·迪古拉

1
是的,要根据预期数据进行权衡。对于具有许多重复项的大型数据集(无需基于磁盘的存储),Perl更好。具有少量重复项的巨大数据集应使用排序(和磁盘存储)。小型数据集可以使用。就个人而言,我将首先尝试Perl,如果失败则切换到排序。
paxdiablo,2009年

因为sort仅在必须交换到磁盘时才给您带来好处。
paxdiablo,2009年

5
当我想要每行的第一次出现时,这很棒。排序会破坏这一点。
Bluu 2012年

10

使用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

2
无需对输入进行排序的明智解决方案。注意事项:只要唯一行的数量足够小(因为将唯一行保留在内存中),非常聪明但神秘的awk解决方案(有关说明,请参见stackoverflow.com/a/21200722/45375)将适用于大型文件。 )。该zsh解决方案首先将整个文件读取到内存中,这对于大文件而言可能不是一个选择。同样,按照书面规定,只有没有嵌入空格的行才能正确处理;要解决此问题,请IFS=$'\n' read -d '' -r -A u <file; print -l ${(u)u}改用。
mklement0 2014年

正确。或:(IFS=$'\n' u=($(<infile)); print -l "${(u)u[@]}")
Dimitre Radoulov 2014年

1
谢谢,这很简单(假设您不需要设置子shell外部所需的变量)。我很好奇您何时需要[@]后缀来引用数组的所有元素-似乎-至少从版本5开始-没有它就可以工作;还是只是为了清楚起见添加了它?
mklement0 2014年

1
@ mklement0,你是对的!我写这篇文章的时候没有想到。实际上,这应该足够了:print -l "${(fu)$(<infile)}"
Dimitre Radoulov 2014年

1
太棒了,感谢您更新您的帖子-我也自由修复了awk示例输出。
mklement0 2014年

9

将它们通过sort和传送uniq。这将删除所有重复项。

uniq -d仅给出重复项,uniq -u仅给出唯一的重复项(条带重复项)。


首先要按它的外观排序
Brabster

1
是的你是。或更准确地说,您需要将所有重复的行分组在一起。排序是通过定义来完成的;)
Matthew Scharley,2009年

另外,uniq -u这不是默认行为(有关详细信息,请参见我的答案中的编辑)
Matthew Scharley 2009年

7

使用AWK,您可以做到,我发现它比排序快

 ./yourscript.ksh | awk '!a[$0]++'

这绝对是我最喜欢的工作方式,非常感谢!特别是对于较大的文件,sort | uniq-solutions可能不是您想要的。
Schmitzi '19

1

根据要求唯一(但不排序);
使用少于70个元素的较少系统资源(经时间测试);
编写为从stdin接收输入
(或修改并包含在另一个脚本中):(重
击)

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[@]}"

0

我得到了一个更好的技巧来获取文件中的非重复条目

awk '$0 != x ":FOO" && NR>1 {print x} {x=$0} END {print}' file_name | uniq -f1 -u
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.