![sed 仅打印该行的第一个模式匹配](https://linux22.com/image/10537/sed%20%E4%BB%85%E6%89%93%E5%8D%B0%E8%AF%A5%E8%A1%8C%E7%9A%84%E7%AC%AC%E4%B8%80%E4%B8%AA%E6%A8%A1%E5%BC%8F%E5%8C%B9%E9%85%8D.png)
我有一些数据,例如
<td><a href="data1">abc</a> ... <a href="data2">abc</a> ... <a href="data3">abc</a>
(将参考上面的行,如data
下面的代码所示)
我需要data1
在第一个之间"
,所以"
我这样做
echo 'data' | sed 's/.*"\(.*\)".*/\1/'
但它返回给我最后一串在之间"
和"
总是之间,即在这种情况下它会返回我data3
而不是data1
为了得到data1
,我最终做了
echo 'data' | sed 's/.*"\(.*\)".*".*".*".*".*/\1/'
我怎样才能data1
在没有这么多冗余的情况下得到sed
答案1
正.*
则表达式模式中的 是贪婪的,它会匹配尽可能长的字符串,因此匹配的引号将是最后的引号。
由于这里的分隔符只有一个字符,因此我们可以使用倒括号组来匹配除引号之外的任何内容,即[^"]
,然后重复该匹配以匹配非引号的多个字符。
$ echo '... "foo" ... "bar" ...' | sed 's/[^"]*"\([^"]*\)".*/\1/'
foo
另一种方法是删除第一个引用之前的所有内容,然后删除从(新的)第一个引用开始的所有内容:
$ echo '... "foo" ... "bar" ...' | sed 's/^[^"]*"//; s/".*$//'
foo
在 Perl 正则表达式中,*
和+
说明符可以通过附加问号来变得非贪婪,.*?
任何东西都可以,但字符/字节尽可能少。
答案2
我不会用关于使用简单正则表达式解析 HTML 的经典警告来烦您。我只想说您应该使用专用的解析器。也就是说,这里的问题是sed
使用贪婪匹配。所以它总是匹配最长的字符串。这意味着您.*
将永远持续下去并匹配整条线。
您可以在sed
(见下文)中执行此操作,但使用允许非贪婪匹配的工具会更简单:
$ perl -pe 's/.*?"(.*?)".*/$1/' file
data1
由于sed
不支持非贪婪匹配,因此您需要一些其他技巧。最简单的方法是使用“不引号”方法伊卡丘的回答。这是一个替代方案:
$ rev file | sed 's/.*"\(.*\)".*/\1/' | rev
data1
这只是反转文件 ( rev
),使用您原来的方法,该方法现在可以工作,因为第一次出现现在是最后一个,然后再次反转文件。
答案3
您可以通过以下几种方式从输入中提取 data1:
grep -oP '^[^"]*"\K[^"]*'
sed -ne '
/\n/!{y/"/\n/;D;}
P
'
perl -lne '/"([^"]*)"/ and print($1),last'
答案4
您还可以使用 Perl 正则表达式的前视和后视来使用非贪婪搜索:
cat data | grep -Po '(?<=href=").*?(?=")' | head -n1