多行正则表达式(grep、sed、awk、perl)

多行正则表达式(grep、sed、awk、perl)

我知道多行正则表达式已经被讨论了几十次,但我就是无法让它与我的模式一起工作。

我会尝试解释一下。我的目录中有一些文本文件。文件中的文本示例:

LINE OF TEXT 2
LINE OF TEXT 1
LINE OF TEXT 3

LINE OF TEXT 1
LINE OF TEXT 2
LINE OF TEXT 3

LINE OF TEXT 1
LINE OF TEXT 3

LINE OF TEXT 3
LINE OF TEXT 2
LINE OF TEXT 1

LINE OF TEXT 2
LINE OF TEXT 3

我想找到“LINE OF TEXT 3”,它位于“LINE OF TEXT 2”之后,而“LINE OF TEXT 2”又位于“LINE OF TEXT 1”之后(中间没有空行)。

每行本身必须是正则表达式,例如一行以“LINE”开头并以特定数字结尾。

注意:并非所有文件都包含确切的行序列,因此如果模式匹配,则不会打印该模式,而只是将文件名打印到 STDOUT。

这可以在单行正则表达式中完成吗?例如,awk 搜索文件中的模式,如果找到模式,则将文件名打印到 STDOUT。然后我可以将此正则表达式与“find -exec”结合使用。

任何提到的工具都可以(grep、awk、sed 或 perl)。

答案1

您可以使用 Awk 通过将“记录分隔符”变量设置为匹配至少两个连续换行符的正则表达式来执行此操作:

awk -v RS='\n\n+' '/1.*2.*3/' file.txt

您还可以将“字段分隔符”设置为单个换行符:

awk -v RS='\n\n+' -F '\n' '$1 == "LINE OF TEXT 1" && $2 == "LINE OF TEXT 2" && $3 == "LINE OF TEXT 3"' file.txt

为了便于阅读而进行了分解:

awk -v RS='\n\n+' -F '\n' '
  $1 == "LINE OF TEXT 1" &&
  $2 == "LINE OF TEXT 2" &&
  $3 == "LINE OF TEXT 3"
' file.txt

如果您要求仅在找到匹配项时打印文件名,您可以这样做:

awk -v RS='\n\n+' -F '\n' '
  $1 == "LINE OF TEXT 1" &&
  $2 == "LINE OF TEXT 2" &&
  $3 == "LINE OF TEXT 3" {
    match++
  }
  END {
    if (match) {
      print FILENAME
    }
' file.txt

但考虑到你正在谈论find结合使用awk,我建议仅使用 Awk 作为退出状态并用于find打印:

find . -type f -exec awk -v RS='\n\n+' -F '\n' '
  $1 ~ /LINE OF TEXT 1/ &&
  $2 ~ /LINE OF TEXT 2/ &&
  $3 ~ /LINE OF TEXT 3/ {
    exit 0
  }
  END { exit 1 }
' {} \; -print

这样,如果你想做某事别的在打印(其他一些find主要内容)之前,您已经做好了这样做的准备。

答案2

您可以使用 Perl 中的“段落模式”,它将按多个换行符分隔的块读取文件。只需将空字符串设置为输入记录分隔符$/

perl -lne 'BEGIN { $/ = "" }
       $found = 1 if /^LINE.* 1\nLINE.* 2\nLINE.* 3$/m;
       if (eof) { print $ARGV if $found; undef $found }
' -- file1 file2...
  • eof每个文件末尾都是 true
  • $ARGV是当前打开的文件的名称。

答案3

您可以使用协同工作的 find<->perl 组合来完成此操作,例如:

find . -type f -exec \
  perl -l -0777ne '/^LINE.* 1\nLINE.* 2\nLINE.* 3$/m && print $ARGV' {} +

相关内容