Regex (grep)用于多行搜索

我正在运行grep来查找任何*。sql文件中包含单词select后跟单词customerName再后跟单词from。这个选择语句可以跨越许多行,并且可以包含制表符和换行符。

我尝试了以下几种方法:

$ grep -liIr --include="*.sql" --exclude-dir="\.svn*" --regexp="select[a-zA-Z0-
9+\n\r]*customerName[a-zA-Z0-9+\n\r]*from"

然而,它会一直运行下去。有人能帮我正确的语法吗?

269030 次浏览

你的基本问题是grep一次工作一行-所以它不能找到跨行分布的SELECT语句。

第二个问题是,您使用的正则表达式没有处理SELECT和FROM之间可能出现的内容的复杂性——特别是,它省略了逗号、句号(句点)和空格,但也省略了引号和任何可以在带引号的字符串内的内容。

我可能会使用基于Perl的解决方案,让Perl每次读取“段落”,并对其应用正则表达式。缺点是必须处理递归搜索——当然,有一些模块可以做这件事,包括核心模块文件::找

在大纲中,对于单个文件:

$/ = "\n\n";    # Paragraphs


while (<>)
{
if ($_ =~ m/SELECT.*customerName.*FROM/mi)
{
printf file name
go to next file
}
}

这需要包装成一个子,然后由File::Find的方法调用。

我不太擅长grep。但是你的问题可以用AWK命令来解决。 看< / p >
awk '/select/,/from/' *.sql

上述代码将从select的第一次出现到from的第一个序列。现在你需要验证返回的语句是否有customername。为此,您可以使用管道输出结果。并且可以再次使用awk或grep。

不需要安装grep变体pcregrep,你可以用grep进行多行搜索。

$ grep -Pzo "(?s)^(\s*)\N*main.*?{.*?^\1}" *.c

解释:

-P activate perl-regexp for grep(正则表达式的强大扩展)

-z将输入视为一组行,每一行以0字节(ASCII NUL字符)而不是换行符结束。也就是说,grep知道行的端点在哪里,但将输入视为一行。注意,如果与-o一起使用,还会添加一个尾随NUL字符,参见注释。

-o只打印匹配。因为我们使用的是-z,整个文件就像一个大行,所以如果有匹配,整个文件将被打印;这样就不会这样了。

在正则表达式:

(?s)激活PCRE_DOTALL,这意味着.找到任何字符或换行符

\N找到除了换行符以外的任何东西,即使激活了PCRE_DOTALL

.*?在非贪婪模式下找到.,即尽快停止。

^查找行开始

\1对第一个组的反向引用(\s*)。这是一种尝试寻找相同缩进的方法。

正如你可以想象的那样,这个搜索将main方法打印在C (*.c)源文件中。