Bash正则表达式:查找包含重复单词的行

3

我有一个包含以下内容的文件:

"def{word}  def{word}"
"def{worad} def{worads}"

我希望报告出现两次的大括号内的单词。 在这种情况下,输出应该只是“word”。 我所拥有的是:

#!/bin/bash
arr=(
   "def{word}  def{word}"
   "def{worad} def{worads}"
)
for i in "${arr[@]}"; do 
   [[ $i =~ def\{([a-z]+)\}.*def\{\1\} ]] || continue
   echo ${BASH_REMATCH[1]}
done

例如,我试图将第一个单词与\1(反向引用)匹配。然而,输出为空。我该怎么做?

你的正则表达式在我的正则表达式测试器中运行良好。所以似乎与bash有关。也许尝试简化,将数组排除,并只在单个字符串上进行测试。 - acarlon
1
但在Bash中这样做似乎不太自然。sed -n s/def{\([^}]*\)} *def{\1}/\1/p' file - tripleee
4个回答

3

我发现在引号中使用bash正则表达式会更加稳定,尽管需要小心一些,因为直接引用会导致精确匹配而非正则表达式匹配。要解决这个问题,你可以将你的正则表达式放在一个被引用的变量中,然后在=~表达式中引用它:

#!/bin/bash
arr=(
   "def{word}  def{word}"
   "def{worad} def{worads}"
)
re="def\{([a-z]+)\}.*def\{\1\}"
for i in "${arr[@]}"; do 
   [[ $i =~ $re ]] || continue
   echo ${BASH_REMATCH[1]}
done

输出:

$ ./worad.sh 
word
$ 

虽然只在Bash v4中有效。


你测试过这个吗?Bash正则表达式不支持反向引用,所以这个不起作用。 - Benjamin W.
@BenjaminW。是的-我测试过了-on bash版本4.x(例如Ubuntu 14.04),其中反向引用确实有效。但是在bash 3.2.x(例如OS X)上不起作用-请参见tripleee的编辑。简化的测试命令:re="([a-z])[0-9]\1"; [[ a1a =~ $re ]] && echo ${BASH_REMATCH[0]} - Digital Trauma
好奇怪,在Bash 4.3.42(MSYS2)中对我不起作用,即使man 3 regex提到了反向引用... - Benjamin W.
为什么 Chet 的 Bash FAQ 只提到反向引用在 Bash 4.3 中“不支持”,并且“可能会在未来实现”? - Benjamin W.
1
@BenjaminW. 我在“ksh-93中的新功能而不是bash-4.3中的新功能”中看到了“模式匹配中的反向引用”,但没有关于正则表达式的反向引用的内容。bash中的模式匹配 != 正则表达式(在bash或其他地方)。 - Digital Trauma
我不熟悉MSYS2,但底层的regexec()调用在这方面可能是不同的。几乎可以肯定,在Windows上你所拥有的版本会与glibc版本不同。 - Digital Trauma

1
使用sed
sed -n '/\({[^{]*}\).*\1/p' file

"def{word}  def{word}"

仅导出该单词

sed  -n 's/.*{\([^{]*\)}.*{\1}.*/\1/p' file

word

0
在bash中,for循环非常慢,这对于bash来说可能有点复杂。我建议使用Python或AWK。以下是用Python实现你想要的功能的代码:
#!/usr/bin/env python

import re
import sys 
import itertools

def freq(alist):
    counts = {}
    for x in alist:
        x = x[1:-1]
        counts[x] = counts.get(x,0) + 1 
    return {m:[j[0] for j in n] for m,n in itertools.groupby(counts.iteritems(), lambda y: y[1])}

for line in sys.stdin:
    counts = freq(re.findall(r'\{[^}]*\}', line))
    if 2 in counts:
        print ' '.join(counts[2])
    else:
        print

假设该脚本在名为two.py的文件中运行,可以运行如下:

cat yourfile | python two.py

现在它已经转换成Python,你拥有了一个更易于扩展和维护的东西。


0

是的,有很多方法可以做到这一点,包括:

perl -lne '/def\{(.+?)\}.*def\{\1\}/ and print $1' filename

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接