假设我有一个包含一些 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匹配的示例中
- 行
a
中没有 的<
。 - 行中的最后一个前导空格以 表示
<
。
所以只有第一行和最后一行,以 a<
作为第一个字符,被省略了。这就是你描述的结果。
为了获取所有不以 a 开头的行<
(忽略前导空格),这应该有效:
grep "^ *[^< ]" somefile.xml
请注意否定模式组中添加的空格字符。
答案2
为什么这不像我期望的那样工作?
使用第一行与您的示例匹配不正确:
<firsttag>一个字符串</firsttag>
让我们分解一下 的匹配grep "^ *[^<]"
。使用^
锚定搜索以匹配以 开头的任何行 *[^<]
。 中有两个前导空格第一行每一个都是匹配的。第一场比赛是第一个' '与^ *
和第二场比赛是第二个 ' ' [^<]
。因此,整行都匹配。这就是其他不需要的行也匹配的原因。
^ *[^< ]
而是使用第二场比赛由于排除了 '<' 和 ' ' 字符,因此会阻止使用空格[^< ]
。因此,匹配的行必须以至少一个非 '<' 或 ' ' 的字符开头。