以下有用的查找命令,打印名称标签中的值
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
以及grep
OpenBSD 和 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