打印带有重复单词的行

时间:2017-01-19 17:41:22

标签: awk sed grep

我正在尝试打印所有可以包含两次或更多相同单词的行

E.g。使用此输入文件:

cat dog cat
dog cat deer
apple peanut banana  apple
car bus train plane
car train car train

输出应为

cat dog cat
apple peanut banana  apple
car train car train.

我已经尝试过这段代码而且它有效,但我认为必须采用更短的方式。

awk '{ a=0;for(i=1;i<=NF;i++){for(j=i+1;j<=NF;j++){if($i==$j)a=1} } if( a==1 ) print $0}'

稍后我想找到所有这些重复的单词并删除除第一次出现之外的所有重复条目。

所以输入:

cat dog cat lion cat 
dog cat deer
apple peanut banana  apple
car bus train plane
car train car train

期望的输出:

cat dog lion 
dog cat deer
apple peanut banana  
car bus train plane
car train

4 个答案:

答案 0 :(得分:3)

您可以使用此GNU sed命令:

sed -rn '/(\b\w+\b).*\b\1\b/ p' yourfile
  • -r激活扩展re,n停用每行的隐式打印
  • p命令只打印与前一个匹配的行(在斜杠内):
    • \b\w+\b are words : an nonemtpy sequence of word charactes ( \ w ) between word boundaries ( \ b`),这些是GNU扩展
    • 由于使用了括号,这句话会被“存储”在\1中供以后重复使用
    • 然后我们尝试将这个词与\b\1\b再次匹配,并在这两个地方之间添加一些可选的(.*)。
    • 这就是整个伎俩:匹配一些东西,把它放在括号中,这样你就可以在\1
    • 中重复使用它

要回答问题的第二部分,删除第一部分之后的双字,但打印所有行(仅修改带有双字的行),你可以使用一些sed s魔法:

sed -r ':A s/(.*)(\b\w+\b)(.*)\b\2\b(.*)/\1\2\3\4/g; t A ;'
  • 这里我们再次使用反向引用技巧。
  • 但我们必须考虑我们的双字之前,之间和之后的事情,因此我们在\2命令的匹配部分中有一个s,我们在替换中有其他反向引用部分。
  • 请注意,只有\2在匹配部分没有parens,我们在替换中使用所有组,因此我们有效地删除了该对中的第二个单词。
  • 为了更多重复我们需要循环的单词:
    • :A是一个标签
    • 如果在最后t A comamnd 中完成了替换,
    • s会跳转到标签
    • 这会在s周围构建一个“while循环”,以删除其他重复

答案 1 :(得分:2)

以下是仅打印包含重复单词的行的解决方案。

awk '{
  delete seen
  for (i=1;i<=NF;++i) {
    if (seen[$i]) { print ; next }
    seen[$i] = 1 
  }
}'

以下是在第一个字词之后删除重复字词的解决方案。

awk '{
  delete seen
  for (i=1;i<=NF;++i) {
    if (seen[$i]) { continue }
    printf("%s ", $i);
    seen[$i] = 1 
  }
  print "";
}'

重新评论......

  

有些人在面对问题时会想“我知道,我会使用正则表达式”。现在他们有两个问题。 - Jamie Zawinski,1997年

答案 2 :(得分:1)

使用egrep,您可以使用所谓的反向引用:

egrep '(\b\w+\b).*\b\1\b' file

(\b\w+\b)匹配捕获组1中词边界处的单词。\1引用匹配模式中的单词。

答案 3 :(得分:0)

我将在Perl中展示解决方案,因为它可能是最灵活的文本解析工具,尤其是在正则表达式方面。

检测重复项

perl -ne 'print if m{\b(\S+)\b.*?(\b\1\b)}g' file

,其中

  • -n导致Perl为每个输入行执行通过-e传递的表达式;
  • \b匹配字边界;
  • \S+匹配一个或多个非空格字符;
  • .*?是一个非greedy匹配零个或多个字符;
  • \1是第一组的backreference,即单词\S+;
  • g全局匹配字符串中的模式。

删除重复项

perl -pe '1 while (s/\b(\S+)\b.*?\K(\s\1\b)//g)' file

,其中

  • -p会导致Perl打印行($_),例如 sed ;
  • 只要替换取代了某些东西,
  • 1 while循环就会运行;
  • \K使该部分与前一个表达式保持匹配;

重复的单词(\s\1\b)将替换为空字符串(//g)。

为什么选择Perl?

Perl正则表达式已知非常灵活,Perl中的正则表达式实际上不仅仅是正则表达式。例如,您可以使用/e修饰符将Perl代码嵌入substitution。您可以使用允许以更易读的格式编写正则表达式的/x修饰符,甚至可以在其中使用Perl注释,例如:

perl -pe '1 while (
  s/            # Begins substitution: s/pattern/replacement/flags
  \b (\S+) \b   # A word
  .*?           # Ungreedy pattern for any number of characters
  \K            # Keep everything that matched the previous patterns
  (             # Group for the duplicate word:
    \s          #   - space
    \1          #   - backreference to the word
    \b          #   - word boundary
  )
  //xg
)' file

正如您应该注意到的,\K anchor非常方便,但is not available in many popular tools包括 awk bash ,以及 SED