【问题标题】:BASH - Tell if duplicate lines exist (y/n)BASH - 判断是否存在重复行(y/n)
【发布时间】:2014-03-18 23:07:52
【问题描述】:

我正在编写一个脚本来操作一个文本文件。

我要做的第一件事是检查是否存在重复条目,如果存在,请询问用户我们是要保留还是删除它们。

如果存在重复行,我知道如何显示它们,但我想学习的只是对“是否存在重复行?”这个问题得到一个是/否的答案

似乎uniq 将返回0,无论是否发现重复,只要命令完成没有问题。

我可以在if-statement 中输入什么命令来告诉我是否存在重复行?

我的文件很简单,只是单列中的值。

【问题讨论】:

  • 如果你不反对使用 Vim 手动过滤文本文件,我推荐 stackoverflow.com/questions/1268032 中的 HighlightRepeats 方法。我经常用它来过滤重复的文件/文件夹,然后在过滤后的文件上应用 shell 命令。
  • @F.X 感谢您的回复。我想用我的脚本中的一些行来完成这个。

标签: bash file uniq


【解决方案1】:

我可能会使用awk 来执行此操作,但为了多样化,这里有一个简短的管道来完成同样的事情:

$ { sort | uniq -d | grep . -qc; } < noduplicates.txt; echo $?
1
$ { sort | uniq -d | grep . -qc; } < duplicates.txt; echo $?
0

sort + uniq -d 确保只有重复的行(不必相邻)被打印到 stdoutgrep . -c 计算那些模拟 wc -l 的行,它具有有用的副作用如果不匹配(即零计数),则返回 1 并且 -q 只是静默输出,因此它不会打印行数,因此您可以在脚本中静默使用它。

has_duplicates()
{
  {
    sort | uniq -d | grep . -qc
  } < "$1"
}

if has_duplicates myfile.txt; then
  echo "myfile.txt has duplicate lines"
else
  echo "myfile.txt has no duplicate lines"
fi

【讨论】:

    【解决方案2】:

    您可以将awk 与布尔|| 运算符结合使用:

    # Ask question if awk found a duplicate
    awk 'a[$0]++{exit 1}' test.txt || (
        echo -n "remove duplicates? [y/n] "
        read answer
        # Remove duplicates if answer was "y" . I'm using `[` the shorthand
        # of the test command. Check `help [`
        [ "$answer" == "y" ] && uniq test.txt > test.uniq.txt
    )
    

    || 之后的块只有在 awk 命令返回 1 时才会被执行,这意味着它发现了重复项。

    但是,为了基本理解,我还将展示一个使用 if 块的示例

    awk 'a[$0]++{exit 1}' test.txt
    
    # $? contains the return value of the last command
    if [ $? != 0 ] ; then
        echo -n "remove duplicates? [y/n] "
        read answer
        # check answer
        if [ "$answer" == "y" ] ; then
            uniq test.txt > test.uniq.txt            
        fi
    fi
    

    不过,[] 只是 方括号,就像在其他编程语言中一样。 [test bash 内置命令的同义词,] 是最后一个参数。你需要阅读help [才能理解

    【讨论】:

    • 感谢您的帮助。我会试试你的代码。
    【解决方案3】:

    快速 bash 解决方案:

    #!/bin/bash
    
    INPUT_FILE=words
    
    declare -A a 
    while read line ; do
        [ "${a[$line]}" = 'nonempty' ] && duplicates=yes && break
        a[$line]=nonempty
    done < $INPUT_FILE
    
    [ "$duplicates" = yes ] && echo -n "Keep duplicates? [Y/n]" && read keepDuplicates
    
    removeDuplicates() {
        sort -u $INPUT_FILE > $INPUT_FILE.tmp
        mv $INPUT_FILE.tmp $INPUT_FILE
    }
    
    [ "$keepDuplicates" != "Y" ] && removeDuplicates
    

    脚本从 INPUT_FILE 中逐行读取,并将每一行作为键存储在关联数组 a 中,并将字符串 nonempty 设置为值。在存储该值之前,它首先检查它是否已经存在 - 如果是,则意味着它发现了一个重复并设置了 duplicates 标志,然后它会退出循环。

    稍后它只检查是否设置了标志并询问用户是否保留重复项。如果他们回答Y 以外的任何其他内容,则它会调用removeDuplicates 函数,该函数使用sort -u 删除重复项。 ${a[$line]} 计算为键 $line 的关联数组 a 的值。 [ "$duplicates" = yes ] 是用于测试的 bash 内置语法。如果测试成功,则评估 &amp;&amp; 之后的任何内容。

    但请注意,awk 解决方案可能会更快,因此如果您希望处理更大的文件,可能需要使用它们。

    【讨论】:

    • 感谢 jkbkot!你能简单解释一下这段代码是如何工作的吗?我是菜鸟:)
    • @DMS 没问题,补充说明。顺便说一句,感谢就足够了;)另外,尝试接受一个答案以保持网站井井有条。编码愉快!
    【解决方案4】:

    您可以使用此 awk 单行代码执行 uniq=yes/no

    awk '!seen[$0]{seen[$0]++; i++} END{print (NR>i)?"no":"yes"}' file
    
    • awk 使用称为 seen 的唯一数组。
    • 每次我们将一个元素放入 unique 中,我们都会增加一个计数器 i++
    • 最后在END 块中,我们将记录数与此代码中的唯一记录数进行比较:(NR&gt;i)?
    • 如果条件为真,则表示存在重复记录,我们打印no,否则打印yes

    【讨论】:

    • 感谢您的回复。你能向我解释一下你的生产线是如何工作的吗?
    • 是的,确实添加了解释。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-02
    • 1970-01-01
    • 1970-01-01
    • 2011-03-13
    • 1970-01-01
    • 2013-05-12
    • 1970-01-01
    相关资源
    最近更新 更多