【问题标题】:Select unique or distinct values from a list in UNIX shell script从 UNIX shell 脚本中的列表中选择唯一或不同的值
【发布时间】:2010-10-11 17:28:56
【问题描述】:

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

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

tar
gz
java
gz
java
tar
class
class

我想查看如下列表:

tar
gz
java
class

【问题讨论】:

    标签: bash unique distinct ksh sh


    【解决方案1】:

    您可能想查看uniqsort 应用程序。

    ./yourscript.ksh |排序 |独特的

    (仅供参考,是的,在此命令行中排序是必需的,uniq 仅删除紧随其后的重复行)

    编辑:

    Aaron Digulla 发布的关于uniq 的命令行选项的内容相反:

    给定以下输入:

    班级 罐 罐 罐 斌 斌 爪哇

    uniq 将所有行仅输出一次:

    班级 罐 斌 爪哇

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

    罐 斌

    uniq -u 将输出恰好出现一次的所有行,并将它们打印一次:

    班级 爪哇

    【讨论】:

    • 仅供迟到者参考:@AaronDigulla 的回答已得到纠正。
    • 非常好的一点,在这个命令行中这个`排序是必要的,uniq只去除我刚刚学到的紧接在彼此之后的重复行!!
    • GNU sort 具有 -u 版本,用于提供唯一值。
    • 我发现uniq 接缝只处理相邻的行(至少默认情况下),这意味着可以在输入uniq 之前输入sort
    • 我对 400MB 的数据进行了一些测试 - sort | uniq 是 95 秒 - sort -u 是 77 - @ajak6 的 awk '!a[$0]++' 是 9 秒。所以 awk 胜出,但也是最难记住的。
    【解决方案2】:

    通过sortuniq 传递它们。这将删除所有重复项。

    uniq -d 只给出重复的,uniq -u 只给出唯一的(去除重复)。

    【讨论】:

    • 先按外观排序
    • 是的,你知道。或者更准确地说,您需要将所有重复的行组合在一起。不过,排序是按定义执行的;)
    • 另外,uniq -u 不是默认行为(有关详细信息,请参阅我的答案中的编辑)
    【解决方案3】:

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

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

    这基本上只是记住每一行输出,这样就不会再输出了。

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

    【讨论】:

    • 请注意,对一个非常大的文件进行排序本身并不是 sort 的问题。它可以对大于可用 RAM+swap 的文件进行排序。如果只有很少的重复项,Perl、OTOH 将失败。
    • 是的,这是一个取舍,取决于预期的数据。 Perl 更适合具有许多重复项的大型数据集(不需要基于磁盘的存储)。具有少量重复的庞大数据集应使用排序(和磁盘存储)。小型数据集可以使用任何一种。就个人而言,我会先尝试 Perl,如果失败则切换到排序。
    • 因为排序只有在必须交换到磁盘时才能为您带来好处。
    • 当我想要每行第一次出现时,这很棒。排序会打破这一点。
    • 最终 perl 将以某种形式对条目进行排序以放入其字典(或在 perl 中调用的任何内容),因此您实际上无法避免排序的处理时间。
    【解决方案4】:

    使用 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 &lt;file; print -l ${(u)u}
    • 正确。或:(IFS=$'\n' u=($(&lt;infile)); print -l "${(u)u[@]}")
    • 谢谢,这更简单(假设您不需要在子外壳之外设置所需的变量)。我很好奇您何时需要 [@] 后缀来引用数组的所有元素 - 似乎 - 至少从版本 5 开始 - 没有它也可以工作;还是您只是为了清楚起见才添加它?
    • @mklement0,你是对的!写这篇文章的时候我没有想到。实际上,这应该足够了:print -l "${(fu)$(&lt;infile)}"
    • 太棒了,感谢您更新您的帖子 - 我也冒昧地修复了 awk 示例输出。
    【解决方案5】:
    ./script.sh | sort -u
    

    这与monoxide'sanswer 相同,但更简洁一些。

    【讨论】:

    • 您是谦虚的:您的解决方案也将执行更好(可能只在大型数据集时才会注意到)。
    • 我觉得应该比... | sort | uniq效率更高,因为是一次性完成的
    • @AdrianAntunez 也许这也是因为sort -u 不需要在每次找到已经遇到的较早值时更新排序列表。而 sort | 必须在将 all 项传递给 uniq 之前对其进行排序
    • @mklement0 @AdrianAntunez 第一次我认为sort -u 可能会更快,因为任何最佳比较排序算法都具有O(n*log(n)) 复杂度,但是可以找到具有O(n) 复杂度的所有唯一值使用 Hash Set 数据结构。尽管如此,sort -usort | uniq 的性能几乎相同,而且它们都很慢。我对我的系统进行了一些测试,更多信息请访问gist.github.com/sda97ghb/690c227eb9a6b7fb9047913bfe0e431d
    【解决方案6】:

    根据要求是唯一的(但未排序);
    使用少于约 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[@]}"
    

    【讨论】:

      【解决方案7】:

      使用 AWK,您可以:

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

      我发现它比 sort 和 uniq 更快

      【讨论】:

      • 这绝对是我最喜欢的工作方式,非常感谢!特别是对于较大的文件,sort|uniq-solutions 可能不是您想要的。
      • 我做了一些测试,这比其他解决方案快 10 倍,但也难以记住 10 倍 :-)
      • 是的,我不太确定 awk 在这里做什么。但是感谢您的解决方案!
      【解决方案8】:

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

      awk '$0 != x ":FOO" && NR>1 {print x} {x=$0} END {print}' file_name | uniq -f1 -u
      
      

      【讨论】:

        猜你喜欢
        • 2016-10-25
        • 1970-01-01
        • 2012-09-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-24
        • 1970-01-01
        相关资源
        最近更新 更多