打印匹配模式 1 的行,除非下一行包含模式 2

Sco*_*Chi 3 grep perl regular-expression

我想匹配包含 foo 的行,除非下一行包含 bar。因此给定一个包含以下内容的文件:

1 foo 1 
foo 2
baz bar bap
Run Code Online (Sandbox Code Playgroud)

只会1 foo 1打印。我在https://regex101.com/r/ZMZsiN/1/foo(?!.*\n.*bar)/上使用负前瞻使其工作,但在命令行上使用 grep 和 perl 使其工作均失败。任何在 perl、sed、awk 或 python 中使用 grep 或单行语句的解决方案都很好。Chatgpt 让我失望了。

一些尝试:

$grep -Pwe 'foo(?!.*\n.*bar)' testfile
1 foo 1 
foo 2
Run Code Online (Sandbox Code Playgroud)
$perl -wnl -e  /'foo(?!\n.*bar)/ and print' testfile
1 foo 1 
foo 2
Run Code Online (Sandbox Code Playgroud)
$perl -ne 'print if /foo/ && ($_ = <>) !~ /bar/' testfile
foo 2
Run Code Online (Sandbox Code Playgroud)

最后一个是基于 chatgpt 提供的内容,并且很接近,但我的 perlfu 还不够好,无法找出问题所在。

Sté*_*las 5

greporperl -n一次只处理一行,因此正则表达式匹配的只是一行的内容(行分隔符甚至不包含在 grepor perlwith中-l)。

您可以使用pcregrep-PGNUgrep可以构建支持使用 PCRE 的选项),它具有带有-M.

pcregrep -M '\bfoo\b(?!.*\n.*\bbar\b)'
Run Code Online (Sandbox Code Playgroud)

除了根据需要将更多行拉入匹配的主题之外,多行模式pcregrep还启用m标志(隐式(?m)),该标志在每行的开头和结尾进行匹配,而不仅仅是主题,并且不^启用标志,这意味着不匹配换行符。$s.

\b用于单词b边界-w不会将单词边界放在有用的地方)。

使用perl -n,您可以将记录分隔符设置为正则表达式不可能在整个文件上匹配的内容:

perl -0777 -ne '
  print for m{^.*\bfoo\b.*\n(?!.*\bbar\b)}mg'
Run Code Online (Sandbox Code Playgroud)

使用标准 Unix 工具箱,您可以使用sed,但标准sed没有字边界运算符,因此您需要笨拙的解决方法:

sed -n '/^\(.*[^[:alnum:]_]\)\{0,1\}foo\([^[:alnum:]_].*\)\{0,1\}$/ {
          $!N
          /\n\(.*[^[:alnum:]_]\)\{0,1\}bar\(.*[^[:alnum:]_]\)\{0,1\}.*$/!P
          D
        }'
Run Code Online (Sandbox Code Playgroud)