在子串提取过程中Sed过多捕获

时间:2016-11-01 13:51:22

标签: bash sed substring matching

我尝试解析卷曲响应,以便检索使用alt标记captcha标识的img src。

为了测试我的sed表达式,我尝试了以下内容:

echo 'alt="captcha" src="http://example.com/foo.html" /></p>' | sed -n 's/.*alt="captcha" src="\([^"]*\)/\1/p'

然而这回声

http://example.com/foo.html" /></p>

我怎样才能简单地返回

http://example.com/foo.html

我是sed的新手,所以我想知道我哪里出错了。

2 个答案:

答案 0 :(得分:3)

这个答案解释了sed的行为,但是123 - 他们在评论中简明扼要地给出了sed问题的正确答案 - 指出了一个更好的选择,如果你有 GNU grepgrep -oP 'alt="captcha" src="\K[^"]*'。 GNU grep的{​​{1}}选项支持PCREs,它们比-P中提供的正则表达式更强大。

该问题与贪婪无关,而是与您的正则表达式仅与该行的部分匹配的事实:

sed中提取子字符串,您的正则表达式必须与整行 匹配。否则,正则表达式匹配 的任何部分只是通过 ,就像您案例中的尾随sed一样;这是一个修复:

" /></p>

请注意我添加的尾随$ echo 'alt="captcha" src="http://example.com/foo.html" /></p>' | sed -n 's/.*alt="captcha" src="\([^"]*\).*/\1/p' http://example.com/foo.html ,这可确保该行的其余部分也匹配。

没有它,匹配后输入行的剩余部分将简单地附加到替换的结果中;即.*部分。 更正确:该行的剩余部分根本不会被替换。

因此,通常,您使用的方法如下(伪表示法):

" /></p>

同样,正则表达式必须与整行匹配才能生效。

由于sed 's/^...<capture-group>...$/\1/p' 的贪婪匹配,您既不需要sed也不需要^,但您可以选择添加它以明确意图。

警告:如果您的捕获组没有歧义, $可以匹配该行的余数,但是{{1} } 之前匹配所有

一个简单示例来演示问题:

.*

请注意.*如何包含$ sed -n 's/[^"]*"\([^"]*\)/>>\1<</p' <<<'before"foo"after' # WRONG >>foo<<"after 按预期捕获的感兴趣的子字符串 - \1之间的字符串\([^"]*\) - 但是,因为正则表达式在之前停止匹配结束foo,该行的剩余部分 - "..." - 仍然输出。

固定版本,附加"以确保整行匹配:

"after

另请注意.*如何用于匹配行的开头到捕获组;由于$ sed -n 's/[^"]*"\([^"]*\).*/>>\1<</p' <<<'before"foo"after' >>foo<< 的贪婪匹配,[^"]*" 在这里工作:

.*

sed贪婪地匹配 last $ sed -n 's/.*"\([^"]*\).*/>>\1<</p' <<<'before"foo"after' # WRONG >>after<< 的所有内容,因此捕获组会捕获.*",这是非{"的运行1}}字符。 关闭后 after

答案 1 :(得分:0)

使用sed分组。它总是我的转到!

Sed正则表达式:

echo 'alt="captcha" src="http://example.com/foo.html" /></p>' | sed 's/\(^alt.*src=\"\)\(.*\)\(\".*p>\)/\2/g'

<强>输出

http://example.com/foo.html