pcregrep 或 grep:使用先行搜索不起作用

pcregrep or grep: searching with lookaheads not working

我正在尝试搜索具有前瞻性的正则表达式,它在 pcregrep 或 grep 中不起作用

我要搜索部分片段

一般我在 sublime text 查找中使用以下内容并且效果很好

(?s)(^PQXY(?:(?!PQXY|OFEJ).)*OFEJ\n)

现在我想找到此类事件的计数,所以我正在尝试使用 grep 或 pcergrep,两者都不起作用。

pcregrep -c "(?s)(^PQXY(?:(?!PQXY|OFEJ).)*OFEJ\n)" file.txt
zsh: event not found: PQXY|OFEJ).)

并使用 grep

$ grep -c -zoP "(?s)(^PQXY(?:(?!PQXY|OFEJTRANS).)*OFEJTRANS\n)" CB_raw_testing_21_feb_CORRECTIONS_0002.txt
zsh: event not found: PQXY|OFEJTRANS).)

我该怎么做

基于@paxdiablo 和@anubha 的回答。

主要错误是 @paxdiablo

指出的单引号
$ pcregrep -c -M '(^PQXY(?:(?!PQXY|OFEJ).)*OFEJ\n)' file.txt 
0

正则解决方案是在@anubha的基础上添加(?s)。当然 \n 也可以代替 (\R|\z)

$ pcregrep -c -M '(?s)(^PQXY(?:(?!PQXY|OFEJ).)*OFEJ\n)' file.txt
11726

zsh: event not found: PQXY|OFEJ).)

由于这是 zsh 引发错误,因此几乎 肯定是 因为它正在尝试处理双引号内的内容。为了保护它免受这种情况的影响,您应该使用 引号,例如:

pcregrep -c '(?s)(^PQXY(?:(?!PQXY|OFEJ).)*OFEJ\n)' file.txt

我没有安装 pcregrep,但这里有一份显示 echo 问题的记录:

pax> echo "(?s)(^PQXY(?:(?!PQXY|OFEJ).)*OFEJ)"
zsh: event not found: PQXY|OFEJ).)

pax> echo '(?s)(^PQXY(?:(?OFEJ)'
(?s)(^PQXY(?:(?OFEJ)

在解决问题而不是使用特定的工具方面,我实际上会选择awk(a) 在这种情况下。你可以这样做:

awk '/^PQXY/     { s = [=12=]; c = 1; next}
     /OFEJ$/     { if (c == 1) { print s""ORS""[=12=]; c = 0 }; next }
     /OFEJ|PQXY/ { c = 0; next }
     c == 1      { s = s""ORS""[=12=] }' inputFile

这通过使用字符串和标志来控制收集的行和状态来工作,最初它们是空字符串和零。

然后,对于每一行:

  • 如果以 PQXY 开头,存储该行并设置收集标志,然后转到下一个输入行。
  • 否则,如果它以OFEJ结尾并且你正在收集,输出收集的部分并停止收集,然后转到下一个输入行。
  • 否则,如果其中有任何一个字符串,则停止收集,移至下一个输入行。
  • 否则,如果收集,追加当前行并(隐式地)移动到下一个输入行。

我用一些有限的测试数据对此进行了测试,它似乎工作正常。这是我用于测试的 bash 脚本(b),您可以根据需要添加尽可能多的测试用例,它可以解决您的问题。

for i in \
    "PQXY 1\nabc\n2 OFEJ\n" \
    "PQXY 1\nabc\n2 OFEJx\n" \
    "PQXY 1\nabc\n  PQXY \n2 OFEJ\n" \
    "PQXY 1\nabc\n  OFEJ \n2 OFEJ\n" \
    "PQXY 1\nabc\ndef\nPQXY 2\n2 OFEJ\n" \
; do
    echo "$i:"
    printf "$i" | awk '
        /^PQXY/     { s = [=13=]; c = 1; next}
        /OFEJ$/     { if (c == 1) { print s""ORS""[=13=]; c = 0 }; next }
        /OFEJ|PQXY/ { c = 0; next }
        c == 1      { s = s""ORS""[=13=] }' | sed 's/^/    /
    '
done

这是输出,您可以看到它的实际效果:

PQXY 1\nabc\n2 OFEJ\n:
    PQXY 1
    abc
    2 OFEJ
PQXY 1\nabc\n2 OFEJx\n:
PQXY 1\nabc\n  PQXY \n2 OFEJ\n:
PQXY 1\nabc\n  OFEJ \n2 OFEJ\n:
PQXY 1\nabc\ndef\nPQXY 2\n2 OFEJ\n:
    PQXY 2
    2 OFEJ

(a) 以我的经验,如果你已经用 grep 风格的正则表达式尝试了三件事但都没有成功,那么转向更多的方法通常会更快高级工具:-)


(b) 是的,我知道它是用 bash 而不是 zsh 写的,但那是因为:

  • 这是一个测试程序,向您展示awk有效,因此使用的语言无关紧要;和
  • 我更喜欢 bash tahn zsh :-)

使用gnu grep:

grep -ozP '(?ms)^PQXY(?:(?!PQXY|OFEJ).)*OFEJ(\R|\z)' file
  • 您必须使用 -z 选项将输入和输出数据视为行序列,每行以零字节结尾。

  • 确保为您的模式使用单引号,这样 shell 的历史模块就不会尝试处理 !

  • 添加了 (?m)(多行)修饰符以允许在正则表达式中为每一行使用 ^$
  • 使用 (\R|\z) 允许结束模式在文件末尾没有换行符的情况下结束。 \R 匹配任何换行符,包括 unicode 字符,\z 匹配输入结尾。

Working Demo


pcregrep

中的等价解
pcregrep -M '(?s)^PQXY(?:(?!PQXY|OFEJ).)*OFEJ(\R|\z)' file

-Mpcregrep 中启用多行选项。