使用sed、awk或perl从行中提取特定模式
如果需要提取由特定图案包围的图案(如果该图案存在于一行中),是否可以使用使用sed、awk或perl从行中提取特定模式,perl,sed,awk,grep,nawk,Perl,Sed,Awk,Grep,Nawk,如果需要提取由特定图案包围的图案(如果该图案存在于一行中),是否可以使用sed 假设我有一个包含以下行的文件: 有许多人不敢自杀,因为担心邻居会说些什么 当我们已经知道答案,但希望我们不知道时,我们会寻求建议 在这两种情况下,我必须扫描行以查找第一个出现的模式,即“[/”或“/*”,并存储以下模式,直到分别退出模式,即“/]”或“*/” 简而言之,我需要恐惧和回答。如果可能,可以扩展到多行;从这个意义上说,如果退出模式发生在不同于相同的行中 欢迎任何形式的建议或算法帮助。提前感谢您的回复 use
sed
假设我有一个包含以下行的文件:
有许多人不敢自杀,因为担心邻居会说些什么
当我们已经知道答案,但希望我们不知道时,我们会寻求建议
在这两种情况下,我必须扫描行以查找第一个出现的模式,即“[/
”或“/*
”,并存储以下模式,直到分别退出模式,即“/
]”或“*/
”
简而言之,我需要恐惧
和回答
。如果可能,可以扩展到多行;从这个意义上说,如果退出模式发生在不同于相同的行中
欢迎任何形式的建议或算法帮助。提前感谢您的回复
use strict;
use warnings;
while (<DATA>) {
while (m#/(\*?)(.*?)\1/#g) {
print "$2\n";
}
}
__DATA__
There are many who dare not kill themselves for [/fear/] of what the neighbors will say.
Advice is what we ask for when we already know the /* answer */ but wish we didn’t.
内部while循环将使用/g
修饰符在所有匹配之间迭代。反向引用\1
将确保我们只匹配相同的打开/关闭标记
如果需要匹配延伸到多条线的块,则需要对输入进行模糊处理:
use strict;
use warnings;
$/ = undef;
while (<DATA>) {
while (m#/(\*?)(.*?)\1/#sg) {
print "$2\n";
}
}
__DATA__
There are many who dare not kill themselves for [/fear/] of what the neighbors will say. /* foofer */
Advice is what we ask for when we already know the /* answer */ but wish we didn’t.
foo bar /
baz
baaz / fooz
-0777
开关和$/=undef
将导致文件slurping,这意味着所有文件都被读入标量。我还添加了/s
修饰符,以允许通配符
匹配换行符
正则表达式的解释:m/(\*?)(.*?)\1/#sg
这里的“魔力”在于,只有在找到一个星号时,反向引用才需要星号。在
awk中快速而肮脏的方式
awk 'NF{ for (i=1;i<=NF;i++) if($i ~ /^\[\//) { print gensub (/^..(.*)..$/,"\\1","g",$i); } else if ($i ~ /^\/\*/) print $(i+1);next}1' input_file
awk'NF{for(i=1;i单线匹配
如果您真的想在sed中这样做,那么可以相对容易地提取分隔模式,只要它们在同一行上
# Using GNU sed. Escape a whole lot more if your sed doesn't handle
# the -r flag.
sed -rn 's![^*/]*(/\*?.*/).*!\1!p' /tmp/foo
多行匹配
如果你想用sed进行多行匹配,事情会变得更糟。但是,这是可以做到的
# Multi-line matching of delimiters with GNU sed.
sed -rn ':loop
/\/[^\/]/ {
N
s![^*/]+(/\*?.*\*?/).*!\1!p
T loop
}' /tmp/foo
诀窍是查找起始分隔符,然后在循环中不断追加行,直到找到结束分隔符
只要您确实有一个结束分隔符,这种方法就可以很好地工作。否则,文件的内容将一直附加到模式空间,直到sed找到一个分隔符,或者直到它到达文件的结尾。这可能会导致sed的某些版本出现问题,或者导致模式空间大小非常大的文件出现问题无法控制
有关更多信息,请参阅。我不确定SED是否可以完成,顺便说一句,我不介意使用perl脚本。至于SED
,请参阅我的:目前没有简单的方法,但可以完成一些事情。@LevLevitsky非常有趣!如果还不够,我一定要再看一遍。感谢添加链接:)它能在多行上匹配吗?干得好,尽管你的正则表达式让我有点不舒服:)@Zaid它已经足够酸了:P@TLP虽然对我来说有点难以理解,但它在我的案例中没有任何问题:)而且解释非常好!非常感谢顶级程序员;)@Geekasaur这是因为你有一个额外的空间。您可以在匹配前后插入`*`(即空格后跟星号),并在替换中插入单个空格来解决此问题。s\*/*(\*?)(*?)(*?)\1*/\sg;
awk 'NF{ for (i=1;i<=NF;i++) if($i ~ /^\[\//) { print gensub (/^..(.*)..$/,"\\1","g",$i); } else if ($i ~ /^\/\*/) print $(i+1);next}1' input_file
$ cat file
There are many who dare not kill themselves for [/fear/] of what the neighbors will say.
Advice is what we ask for when we already know the /* answer */ but wish we didn't.
$ awk 'NF{ for (i=1;i<=NF;i++) if($i ~ /^\[\//) { print gensub (/^..(.*)..$/,"\\1","g",$i); } else if ($i ~ /^\/\*/) print $(i+1);next}1' file
fear
answer
# Using GNU sed. Escape a whole lot more if your sed doesn't handle
# the -r flag.
sed -rn 's![^*/]*(/\*?.*/).*!\1!p' /tmp/foo
# Multi-line matching of delimiters with GNU sed.
sed -rn ':loop
/\/[^\/]/ {
N
s![^*/]+(/\*?.*\*?/).*!\1!p
T loop
}' /tmp/foo