如何使用换行符或制表符或空格来 grep 多行字符串
How to grep multi line string with new line characters or tab characters or spaces
我的测试文件包含如下文本:
> cat test.txt
new dummy("test1", random1).foo("bar1");
new dummy("
test2", random2);
new dummy("test3", random3).foo("bar3");
new dummy = dummy(
"test4", random4).foo("bar4");
我正在尝试匹配所有以分号 (;) 结尾并包含文本“dummy(”的单行。然后我需要提取 dummy 内双引号中的字符串。我想出了以下命令, 但它只匹配第一条和第三条语句。
> perl -ne 'print if /dummy/ .. /;/' test.txt | grep -oP 'dummy\((.|\n)*,'
dummy("test1",
dummy("test3",
使用 -o 标志,我希望在 dummy 中的双引号之间提取字符串。但这也行不通。你能告诉我如何进行吗?
预期输出为:
test1
test2
test3
test4
下面的一些答案适用于基本文件结构。如果 lines 包含超过 1 个换行符,则代码中断。例如输入换行符较多的文本文件:
new dummy("test1", random1).foo("bar1");
new dummy("
test2", random2);
new dummy("test3", random3).foo("bar3");
new dummy = dummy(
"test4", random4).foo("bar4");
new dummy("test5",
random5).foo("bar5");
new dummy("test6", random6).foo(
"bar6");
new dummy("test7", random7).foo("
bar7");
我参考了以下 SO 链接:
How to give a pattern for new line in grep?
how to grep multiple lines until ; (semicolon)
@TLP 非常接近:
perl -0777 -nE 'say for map {s/^\s+|\s+$//gr} /\bdummy\(\s*"(.+?)"/gs' test.txt
test1
test2
正在使用
-0777
将文件作为单个字符串插入
/\bdummy\(\s*"(.+?)"/gs
查找“dummy(”之后所有引用的字符串内容(在引号前有可选的空格)
s
标志允许 .
匹配换行符。
- 任何包含转义双引号的字符串都会破坏此正则表达式
map {s/^\s+|\s+$//gr}
从每个字符串中删除 leading/trailing 空格。
鉴于:
$ cat file
new dummy("test1", random1).foo("bar1");
new dummy("
test2", random2);
new dummy("test3", random3).foo("bar3");
new dummy = dummy(
"test4", random4).foo("bar4");
您可以这样使用 GNU grep:
$ grep -ozP '[^;]*\bdummy[^";]*"\s*\K[^";]*[^;]*;' file | tr '[=11=]0' '\n' | grep -oP '^[^"]*'
test1
test2
test3
test4
更稳健一些,如果这是 ;
分隔文本,您可以:
- 拆分
;
;
- 过滤
/\bdummy\b/
;
- 获取引号中的第一个字段;
- 去掉空格。
这是 ruby
中的所有内容:
ruby -e 'puts $<.read.split(/(?<=;)/).
select{|b| b[/\bdummy\b/]}.
map{|s| s[/(?<=")[^"]*/].strip}' file
# same output
这个 perl
应该有效:
perl -0777 -pe 's/(?m)^[^(]* dummy\(\s*"\s*([^"]+).*//g' file
test1
test2
test3
test4
以下 gnu-grep + tr
也应该有效:
grep -zoP '[^(]* dummy\(\s*"\s*\K[^"]+"' file | tr '"' '\n'
test1
test2
test3
test4
您可以使用 Text::ParseWords
来提取引用的字段。
use strict;
use warnings;
use Data::Dumper;
use Text::ParseWords;
my $str = do {
local $/;
<DATA>;
}; # slurp the text into a variable
my @lines = quotewords(q("), 1, $str); # extract fields
my @txt;
for (0 .. $#lines) {
if ($lines[$_] =~ /\bdummy\s*\(/) {
push @txt, $lines[$_+1]; # target text will be in fields following "dummy("
}
}
s/^\s+|\s+$//g for @txt; # trim leading/trailing whitespace
print Dumper \@txt;
__DATA__
new dummy("test1", random1).foo("bar1");
new dummy("
test2", random2);
new dummy("test3", random3).foo("bar3");
new dummy = dummy(
"test4", random4).foo("bar4");
输出:
$VAR1 = [
'test1',
'test2',
'test3',
'test4'
];
使用您展示的示例,请尝试遵循 awk
代码,用 GNU awk
编写和测试。
awk -v RS='(^|\n)new[^;]*;' '
RT{
rt=RT
gsub(/\n+|[[:space:]]+/,"",rt)
match(rt,/"[^"]*"/)
print substr(rt,RSTART+1,RLENGTH-2)
}
' Input_file
基于 awk
的解决方案通过 FS
处理一切:
<test1.txt gawk -b -e 'BEGIN { RS="^$"
FS="((^|\n)?"(___="[^\n")"]+y[(]"(_="[ \t\n]*")(__="[\42]")(_)\
"|"(_="[ \t]*")(__)(_)"[,]"(___)";]+[;][\n])+"} sub(OFS=ORS,"",$!--NF)'
test1
test2
test3
test4
gawk
在 5.15 secs
的 2 million rows
进行了基准测试,因此除非您的输入文件超出 100 MB
,否则就足够了。
*** 警告:避免将 mawk-1.9.9.6
与此解决方案一起使用
建议简单的 gawk
脚本(标准 linux awk
):
awk '/dummy/{print gensub("[[:space:]]*","",1,)}' RS=';' FS='"' input.txt
解释:
RS=';'
将 awk
记录分隔符设置为 ;
FS='"'
将 awk
字段分隔符设置为 "
/dummy/
仅过滤匹配dummy
RexExp
的记录
gensub("[[:space:]]*","",1,)
Trim 从第二个字段开始的任何 white-spaces
print gensub("[[:space:]]*","",1,)
打印修剪的第二个字段
我的测试文件包含如下文本:
> cat test.txt
new dummy("test1", random1).foo("bar1");
new dummy("
test2", random2);
new dummy("test3", random3).foo("bar3");
new dummy = dummy(
"test4", random4).foo("bar4");
我正在尝试匹配所有以分号 (;) 结尾并包含文本“dummy(”的单行。然后我需要提取 dummy 内双引号中的字符串。我想出了以下命令, 但它只匹配第一条和第三条语句。
> perl -ne 'print if /dummy/ .. /;/' test.txt | grep -oP 'dummy\((.|\n)*,'
dummy("test1",
dummy("test3",
使用 -o 标志,我希望在 dummy 中的双引号之间提取字符串。但这也行不通。你能告诉我如何进行吗?
预期输出为:
test1
test2
test3
test4
下面的一些答案适用于基本文件结构。如果 lines 包含超过 1 个换行符,则代码中断。例如输入换行符较多的文本文件:
new dummy("test1", random1).foo("bar1");
new dummy("
test2", random2);
new dummy("test3", random3).foo("bar3");
new dummy = dummy(
"test4", random4).foo("bar4");
new dummy("test5",
random5).foo("bar5");
new dummy("test6", random6).foo(
"bar6");
new dummy("test7", random7).foo("
bar7");
我参考了以下 SO 链接:
How to give a pattern for new line in grep?
how to grep multiple lines until ; (semicolon)
@TLP 非常接近:
perl -0777 -nE 'say for map {s/^\s+|\s+$//gr} /\bdummy\(\s*"(.+?)"/gs' test.txt
test1
test2
正在使用
-0777
将文件作为单个字符串插入/\bdummy\(\s*"(.+?)"/gs
查找“dummy(”之后所有引用的字符串内容(在引号前有可选的空格)s
标志允许.
匹配换行符。- 任何包含转义双引号的字符串都会破坏此正则表达式
map {s/^\s+|\s+$//gr}
从每个字符串中删除 leading/trailing 空格。
鉴于:
$ cat file
new dummy("test1", random1).foo("bar1");
new dummy("
test2", random2);
new dummy("test3", random3).foo("bar3");
new dummy = dummy(
"test4", random4).foo("bar4");
您可以这样使用 GNU grep:
$ grep -ozP '[^;]*\bdummy[^";]*"\s*\K[^";]*[^;]*;' file | tr '[=11=]0' '\n' | grep -oP '^[^"]*'
test1
test2
test3
test4
更稳健一些,如果这是 ;
分隔文本,您可以:
- 拆分
;
; - 过滤
/\bdummy\b/
; - 获取引号中的第一个字段;
- 去掉空格。
这是 ruby
中的所有内容:
ruby -e 'puts $<.read.split(/(?<=;)/).
select{|b| b[/\bdummy\b/]}.
map{|s| s[/(?<=")[^"]*/].strip}' file
# same output
这个 perl
应该有效:
perl -0777 -pe 's/(?m)^[^(]* dummy\(\s*"\s*([^"]+).*//g' file
test1
test2
test3
test4
以下 gnu-grep + tr
也应该有效:
grep -zoP '[^(]* dummy\(\s*"\s*\K[^"]+"' file | tr '"' '\n'
test1
test2
test3
test4
您可以使用 Text::ParseWords
来提取引用的字段。
use strict;
use warnings;
use Data::Dumper;
use Text::ParseWords;
my $str = do {
local $/;
<DATA>;
}; # slurp the text into a variable
my @lines = quotewords(q("), 1, $str); # extract fields
my @txt;
for (0 .. $#lines) {
if ($lines[$_] =~ /\bdummy\s*\(/) {
push @txt, $lines[$_+1]; # target text will be in fields following "dummy("
}
}
s/^\s+|\s+$//g for @txt; # trim leading/trailing whitespace
print Dumper \@txt;
__DATA__
new dummy("test1", random1).foo("bar1");
new dummy("
test2", random2);
new dummy("test3", random3).foo("bar3");
new dummy = dummy(
"test4", random4).foo("bar4");
输出:
$VAR1 = [
'test1',
'test2',
'test3',
'test4'
];
使用您展示的示例,请尝试遵循 awk
代码,用 GNU awk
编写和测试。
awk -v RS='(^|\n)new[^;]*;' '
RT{
rt=RT
gsub(/\n+|[[:space:]]+/,"",rt)
match(rt,/"[^"]*"/)
print substr(rt,RSTART+1,RLENGTH-2)
}
' Input_file
awk
的解决方案通过 FS
处理一切:
<test1.txt gawk -b -e 'BEGIN { RS="^$"
FS="((^|\n)?"(___="[^\n")"]+y[(]"(_="[ \t\n]*")(__="[\42]")(_)\
"|"(_="[ \t]*")(__)(_)"[,]"(___)";]+[;][\n])+"} sub(OFS=ORS,"",$!--NF)'
test1
test2
test3
test4
gawk
在 5.15 secs
的 2 million rows
进行了基准测试,因此除非您的输入文件超出 100 MB
,否则就足够了。
*** 警告:避免将 mawk-1.9.9.6
与此解决方案一起使用
建议简单的 gawk
脚本(标准 linux awk
):
awk '/dummy/{print gensub("[[:space:]]*","",1,)}' RS=';' FS='"' input.txt
解释:
RS=';'
将 awk
记录分隔符设置为 ;
FS='"'
将 awk
字段分隔符设置为 "
/dummy/
仅过滤匹配dummy
RexExp
gensub("[[:space:]]*","",1,)
Trim 从第二个字段开始的任何 white-spaces
print gensub("[[:space:]]*","",1,)
打印修剪的第二个字段