find+当grep匹配时如何打印文件名

find+当grep匹配时如何打印文件名

以下有用的查找命令,打印名称标签中的值

find /tmp -type f -name '*.xml' -exec grep -o -P '(?<=<Name>).*(?=</Name>)' {} \;

问题是,如果 /tmp 下有几个 xml 文件,那么我们永远不知道哪个 xml 文件具有名称标签

或者换句话说,这个 find 语法将打印 Name 的值

但没有 xml 文件名

请建议当 grep 匹配时如何打印文件名:

(?<=<Name>).*(?=</Name>)

答案1

要使用适当的 XML 解析器(这里我使用)来提取文件名后缀位于 或之下的所有 XML 文件中的xmlstarlet所有节点的值:Name.xml/tmp

find /tmp -type f -name '*.xml' -exec xmlstarlet sel -t -v '//Name' -nl {} + 

这确实不是要求<Name>开始标记和相应的</Name>结束标记位于同一行,也不要求节点Name没有属性,就像您的grep命令那样。

要使用 输出更多信息xmlstarlet,例如当前正在处理的文件名,并且仅在文件实际具有节点时才执行此操作Name,请将上面命令xmlstarlet中的调用替换为find

xmlstarlet sel -t -i '//Name' -o '### ' -f -o ':' -nl -v '//Name' -nl

这将输出 XML 文件的路径名,前缀###和后缀为:换行符,但前提是该文件包含Name节点。之后是Name文档中每个节点的值。


使用grep

grep如果命令行上给出了多个文件,则将始终输出包含匹配项的文件的文件名。如果只传递一个文件,则不会打印任何文件名。

要强制始终打印文件名以及实际匹配项,请将其/dev/null作为额外文件添加到 grep 中:

find /tmp -type f -name '*.xml' -exec grep -o -P '(?<=<Name>).*(?=</Name>)' {} /dev/null \;

或者,为了可能减少对 的调用grep,请find -exec grep ... {} +改用:

find /tmp -type f -name '*.xml' -exec grep -o -P '(?<=<Name>).*(?=</Name>)' /dev/null {} +

至少 GNUgrep以及grepOpenBSD 和 FreeBSD 也支持-H始终打印文件名的标志,即使只给出一个文件。既然您使用了grep -P,那么您可能正在使用 GNU grep

答案2

您只需给 greps“-H”参数,那么文件名将始终被打印,即使只有一个文件可供 grep 处理(如您的情况)。

答案3

请注意,grep这不是解析 xml/html 文件(文档)的正确工具,并且不会提供强大且可靠的解决方案。使用“正确的”xml/html 解析器,例如xmlstarlet:

find /tmp -type f -name '*.xml' -exec xmlstarlet sel -t -m "//Name" -f -n {} \;
  • xmlstarlet sel -t -m "//Name" -f -n-f-仅当输入 xml 文档匹配 ( -m) XPATH 表达式时才会打印输入文件名(由选项确保)"//Name"

答案4

如果您不介意在找到的行之后打印文件名,则总是可以找到“-print”选项:

find /tmp -type f -name '*.xml' -exec grep -o -P '(?<=<Name>).*(?=</Name>)' {} \; -print

相关内容