sed 仅打印该行的第一个模式匹配

sed 仅打印该行的第一个模式匹配

我有一些数据,例如

<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

相关内容