需要用于多行搜索的正则表达式(grep)[重复]

问题描述 投票:0回答:3

我正在运行

grep
来查找任何包含单词
select
后跟单词
customerName
后跟单词
from
的 *.sql 文件。此 select 语句可以跨越多行,并且可以包含制表符和换行符。

我尝试了以下一些变体:

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

然而,这只是永远运行。谁能帮我正确的语法吗?

regex linux cygwin grep
3个回答
629
投票

无需安装 grep 变体

pcregrep
,您就可以使用 grep 进行多行搜索。

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

说明:

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

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

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

在正则表达式中:

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

\N
即使激活了
PCRE_DOTALL
,也能找到除换行符之外的任何内容

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

^
找到行首

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

正如您所想,此搜索会打印 C (

*.c
) 源文件中的 main 方法。


214
投票

我不太擅长 grep。但是您的问题可以使用 AWK 命令来解决。 就看吧

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

上面的代码将从

select
第一次出现到
from
的第一个序列产生。现在您需要验证返回的语句是否有
customername
。为此,您可以通过管道传输结果。并且可以再次使用 awk 或 grep。


8
投票

您的根本问题是

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 的方法调用。

© www.soinside.com 2019 - 2024. All rights reserved.