我知道多行正则表达式已经被讨论了几十次,但我就是无法让它与我的模式一起工作。
我会尝试解释一下。我的目录中有一些文本文件。文件中的文本示例:
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' {} +