1

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

我想搜索一些部分

  • 可能跨越多行,
  • 在行首以 PQXY 开头,并且
  • 在行尾以 OFEJ 结尾,并且
  • 两者之间不包含 PQXY 或 OFEJ

一般我在崇高的文本查找中使用以下内容并且效果很好

(?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
4

2 回答 2

2

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 = $0; c = 1; next}
     /OFEJ$/     { if (c == 1) { print s""ORS""$0; c = 0 }; next }
     /OFEJ|PQXY/ { c = 0; next }
     c == 1      { s = s""ORS""$0 }' 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 = $0; c = 1; next}
        /OFEJ$/     { if (c == 1) { print s""ORS""$0; c = 0 }; next }
        /OFEJ|PQXY/ { c = 0; next }
        c == 1      { s = s""ORS""$0 }' | 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-style regex 尝试了三件事但没有成功,那么转向更高级的工具通常会更快:-)


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

  • 这是一个向您展示有效的测试程序awk,因此使用的语言无关紧要;和
  • 我对bashtahn更满意zsh:-)
于 2020-02-24T05:28:08.423 回答
2

使用gnu grep

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

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

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

工作演示


等效解决方案pcregrep

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

-M启用多行选项pcregrep

于 2020-02-24T05:31:07.200 回答