我正在运行一个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"

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


您的基本问题是grep一次处理一行—因此它无法找到跨行分布的SELECT语句。

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

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

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

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

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

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


我不太擅长grep。但是你的问题可以用AWK命令来解决。 只看到

awk '/select/,/from/' *.sql

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


不需要安装grep变体pcregrep,您可以使用grep执行多行搜索。

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

解释:

为grep激活perl-regexp(正则表达式的强大扩展)

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

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

在正则表达式:

(?s)激活PCRE_DOTALL,这意味着。查找任意字符或换行符

\N发现任何除了换行,即使PCRE_DOTALL激活

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

^找到行开始

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

可以想象,这个搜索将主方法打印在C (*. C)源文件中。