使用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