【问题标题】:Removing duplicates in bash string using awk使用 awk 删除 bash 字符串中的重复项
【发布时间】:2017-09-12 21:00:02
【问题描述】:

当我发现它没有按预期工作时,我试图应用这里提出的方法 {Removing duplicates on a variable without sorting} 来使用 awk 删除字符串中的重复项。

例如,假设我们有:

s="apple apple tree appleapple tree"

删除重复项我们期望得到以下输出:

apple tree appleaplle

应该通过将以下命令应用于字符串来获得(完整说明在链接中):

awk 'BEGIN{RS=" "; ORS=" "}{ if(a[$0] == 0){a[$0]+=1; print $0}}' <<< $s

它使用关联数组,因此我们不希望打印两次相同的记录。但是,按照这种方法,我得到了这个

 apple tree appleapple tree

第一个 apple 副本已按需要删除,但不是最后一个。 实际上,如果我们打印每条记录的长度,我们会看到最后一条记录不是tree,而是tree+ 返回字符(我想是这样)。

$ awk 'BEGIN{RS=" "; ORS=" "}{ print length($0); print $0}' <<< $s
$ 5 apple 5 apple 4 tree 10 appleapple 5 tree

注意最后一棵树确实是 5 个字符而不是 4 个字符,导致破坏了关联数组方法。

我不明白为什么会有这个角色,它是从哪里来的? 以及如何解决此问题以使用此方法删除重复项?

非常感谢您的任何建议

【问题讨论】:

  • 使用od -c scriptfile查看你的文件是否有CR+LF行尾,使用dos2unix修复。
  • 这一次不是 CR+LF 问题,而是简单的飞行员错误。

标签: bash awk


【解决方案1】:

如果不需要维护词序:

$ ( set -f; printf "%s\n" $s | sort -u | paste -sd" " )
apple appleapple tree

如果您确实想保留订单:

$ awk '                                                                                                      
    {          
        delete seen
        sep=""
        for (i=1; i<=NF; i++) {
            if (!seen[$i]++) {
                printf "%s%s", sep, $i
            }
            sep=OFS
        }
        print ""
    }
' <<<"$s"
apple tree appleapple

【讨论】:

  • 谢谢。我会选择第二个答案,因为我还不熟悉排序和过去。正如您和@MarcLambrichs 在另一个答案中所建议的那样,使用字段似乎可以避免这个问题。尽管如此,我仍然不明白使用记录出了什么问题。
  • 您使用记录的方法的问题在于,当您设置RS=" " 时,这意味着您行尾的\n 是最终字段的一部分,而tree 不是与tree\n 相同。如果您在输入字符串的末尾添加了一个空白字符并正确引用它(&lt;&lt;&lt; "$s ")或设置RS="[[:space:]]+" 而不是RS=" ",它会认为后者是 gawk 特定的,因为多字符 RS .
  • 好吧,我现在明白了。事实上,我尝试在最后添加一个额外的空白并且它正在工作,但对那个“解决方案”并不满意。现在问题很清楚了。
【解决方案2】:

如前所述,通过将 RS 设置为 " ",这意味着 \n 不再是记录之间的字符,因此它成为输入行 "tree\n" 上最后一个字段的一部分。

FWIW 如果你有 GNU awk for multi-char RS 你可以这样做:

awk -v RS='\\s+' '!seen[$0]++{printf "%s%s", (NR>1?OFS:""), $0} END{print ""}'

【讨论】:

  • 经过你的解释,一清二楚。没有留下任何谜团。使用该方法需要记录的多字符分隔符。
【解决方案3】:

这个例子表明你的怀疑是正确的:

$ echo "apple apple tree appleapple tree" | awk 'BEGIN{RS=" "; ORS=" "}
{ printf("%s |%s| ", length($0), $0)}'
5 |apple| 5 |apple| 4 |tree| 10 |appleapple| 5 |tree
|

我会使用 FS 来获取所有不同的值,如下所示:

$ echo "apple apple tree appleapple tree" | awk '{for (i=1; i<=NF; i++) 
printf "%s %s\n", length($i), $i}'
5 apple
5 apple
4 tree
10 appleapple
4 tree

并摆脱双打:

echo "apple apple tree appleapple tree" | awk 'BEGIN{ORS=" "}{for (i=1; 
i<=NF; i++)a[$i]++} END {for (i in a) print i }'

【讨论】:

  • 谢谢,是的,使用字段而不是记录似乎是实现这一目标的更好方法
【解决方案4】:

这是我对重复记录所做的:

awk '{if(arr[$1]!="true") print $1; arr[$1]="true"}' file.txt

【讨论】:

    猜你喜欢
    • 2018-03-24
    • 1970-01-01
    • 1970-01-01
    • 2016-10-24
    • 2020-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-22
    相关资源
    最近更新 更多