我正在运行
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"
然而,这只是永远运行。谁能帮我正确的语法吗?
无需安装 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 方法。
我不太擅长 grep。但是您的问题可以使用 AWK 命令来解决。 就看吧
awk '/select/,/from/' *.sql
上面的代码将从
select
第一次出现到 from
的第一个序列产生。现在您需要验证返回的语句是否有customername
。为此,您可以通过管道传输结果。并且可以再次使用 awk 或 grep。
您的根本问题是
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 的方法调用。