为什么 `grep "^ *[^

为什么 `grep "^ *[^

假设我有一个包含一些 xml 的文件,如下所示:

<headtag>
  <firsttag>a string</firsttag>
  <secondtag>a string</secondtag>
  <subleveltag>
    a line with no tag
  </subleveltag>
</headtag>

我希望能够 grep 任何<在开头没有开始标记(或者更具体地说没有前面的标记)的行。

我发现:

grep -v "^ *<" <somefile.xml

按预期工作:

a line with no tag

但是,当我尝试时:

grep "^ *[^<]" <somefile.xml

在我看来,这似乎是“抓取以零个或多个空格开头的一行,但不包括紧随其后的空格<。然而我得到的却是这个:

  <firsttag>a string</firsttag>
  <secondtag>a string</secondtag>
  <subleveltag>
    a line with no tag
  </subleveltag>

为什么这不像我期望的那样工作?

注意:我不是尝试以这种方式解析 xml。我很清楚我不应该使用 grep 解析 xml。

我在 RHEL 7something 上运行 GNU bash 4.2.46(2)、GNU grep 2.20。

答案1

这种模式欺骗了你:

grep "^ *[^<]" <somefile.xml

正则表达式模式的含义是:

^ * “前导空格可以有一个或多个,但空格不是必需的。”

[^<]“匹配任何非<.
=> 的字符,这也匹配空格字符。

由于^ *可能是空字符串,因此任何不以 开头的行<都会匹配。

[^<]在使用漂亮的 XML匹配的示例中

  1. a中没有 的<
  2. 行中的最后一个前导空格以 表示<

所以只有第一行和最后一行,以 a<作为第一个字符,被省略了。这就是你描述的结果。


为了获取所有不以 a 开头的行<(忽略前导空格),这应该有效:

grep "^ *[^< ]" somefile.xml

请注意否定模式组中添加的空格字符。

答案2

为什么这不像我期望的那样工作?

使用第一行与您的示例匹配不正确:

<firsttag>一个字符串</firsttag>

让我们分解一下 的匹配grep "^ *[^<]"。使用^锚定搜索以匹配以 开头的任何行 *[^<]。 中有两个前导空格第一行每一个都是匹配的。第一场比赛是第一个' '与^ *第二场比赛是第二个 ' ' [^<]。因此,整行都匹配。这就是其他不需要的行也匹配的原因。

^ *[^< ]而是使用第二场比赛由于排除了 '<' 和 ' ' 字符,因此会阻止使用空格[^< ]。因此,匹配的行必须以至少一个非 '<' 或 ' ' 的字符开头。

相关内容