AWK 从 XML 元素获取属性值

时间:2021-02-02 23:08:05

标签: xml awk

大家好,我正在尝试使用 AWK 从 XML 文件中提取 version= 中的 pkg-info 值。

我只想做这样的事情:

cat file_below.txt | awk some_commands

使用下面的数据

<?xml version="1.0" encoding="utf-8"?>
<pkg-info overwrite-permissions="true" relocatable="false" identifier="com.application.something" version="1.2.3" format-version="2" generator-version="ABC" install-location="/Applications" auth="root">
</pkg-info>

所需的输出是:

1.2.3

提前致谢

5 个答案:

答案 0 :(得分:2)

一种简单的方法是使用 sed 定位以 "<pkg-info..." 开头的行,然后使用捕获版本并重新插入作为反向引用的替换来隔离版本,例如

sed -E -n '/^<pkg-info/s/^.*[ ]version="([^"]+)".*$/\1/p' file

其中 -E 指定扩展正则表达式,-n 禁止模式空间的正常输出,并且:

  • /^<pkg-info/ 定位以 "<pkg-info" 开头的行,然后是正常的
  • s/find/replace/ 替换,其中 find 是:
  • ^.*[ ]version="([^"]+)".*$ 忽略从行首到空格后跟 version=" 的字符,捕获组 ([^"]+) 捕获后面不是 '"' 的一个或多个字符(即您想要的版本号),然后 ".*$ 从结束 '"' 到行尾忽略。
  • 替换是 \1,它只是插入第一个引用(上面第一个捕获组中捕获的内容),并且
  • /p 然后打印结果。

示例使用/输出

使用 file 中的示例,您将:

$ sed -E -n '/^<pkg-info/s/^.*[ ]version="([^"]+)".*$/\1/p' file
1.2.3

答案 1 :(得分:2)

您拥有的是一个 XML Element,其中包含您希望获得的属性=值组合。

虽然您可以有一个简单的 awksed 来从您拥有的单行示例中检索 1.2.3,但您确实应该 使用 XML parser。如果您不这样做,它将来可能无法工作。

虽然您已经给出了以下所有属性的单行示例:

<?xml version="1.0" encoding="utf-8"?>
<pkg-info overwrite-permissions="true" relocatable="false" identifier="com.application.something" version="1.2.3" format-version="2" generator-version="ABC" install-location="/Applications" auth="root">
</pkg-info>

同样的数据也很容易:

<?xml version="1.0" encoding="utf-8"?>
<pkg-info overwrite-permissions="true" 
          relocatable="false" identifier="com.application.something" 
          version="1.2.3" format-version="2" 
          generator-version="ABC" install-location="/Applications" auth="root">
</pkg-info>

或者,

<?xml version="1.0" encoding="utf-8"?><pkg-info overwrite-permissions="true" relocatable="false" identifier="com.application.something" version="1.2.3" format-version="2" generator-version="ABC" install-location="/Applications"  auth="root"/>

并且仍然被解析为相同的数据。所有三个示例都是有效的 XML,但这里的 awk 或 sed 答案都不能处理第一个示例。

对于 XML,'\n'' ''\t''\r' 都是相同的1 awk 和 sed 那些字符有非常不同的含义。尝试强制像 awk 或 sed 这样的面向行的工具来处理像 XML 这样的面向标签的数据是极其脆弱的

处理此问题的最佳方法是使用 XPath 查询。相关查询将是:

/pkg-info/@version

DEMO

鉴于 file 具有上述某种有效形式的 XML,您可以使用其中一种方法。

这是一个简单的 Ruby 示例。使用 nokogiri xml 解析器通过 xpath 解析感兴趣的属性:

ruby -r nokogiri -e 'doc=Nokogiri::XML($<.read)
puts doc.xpath("/pkg-info").attribute("version").value' file
1.2.3

(您可能需要在系统上安装带有 gem install nokogiri 的 nokogiri...)

或使用XMLStarlet

xml sel -t -v '/pkg-info/@version' file
1.2.3

如果您的 Perl 安装了 XML::XPath 模块(大多数系统都安装了),那么您还有一个名为 xpath 的命令行 XPath 查询工具。你可以这样做:

xpath -q -e '/pkg-info/@version' file
 version="1.2.3"

然后通过 sed 运行它以获取值:

xpath -q -e '/pkg-info/@version' file | sed -E 's/[^"]*"([^"]*).*/\1/'
1.2.3 

请注意,XML 解析器可以处理您的 XML 数据的任何合法版本。此处的其他 sedawk 解决方案不会。


如果您wreally wreally wreally想要使用正则表达式,Perl 是更好的选择。这适用于上述所有三个示例:

perl -0777 -lnE 'say $1 if /(?:\s|>)<pkg-info[\s\S]*?\sversion="([^"]+)"/m' file

如果你hafta hafta hafta有一个awk,你可以设置RS-"^$",它的作用是将整个文件作为一个字符串读入,然后:

  1. "<pkg-info " 找出要点。
  2. 由于这些是属性而不是嵌套标签,因此属性部分中将没有 >。但是,无论 <pkg-info 元素如何终止,都必须有一个 > 来终止它。
  3. 现在将 ' version=" 值两侧的所有内容都用 ""
  4. 印刷和盈利。

awk 适用于我的所有示例;但是,您确实应该使用 XML 解析器。

awk -v RS="^$" '{ x=index($0, "<pkg-info ")
                  s=substr($0,x)
                  sub(/[^>]*\sversion="/,"", s)
                  sub(/".*/,"", s)
                  print s
                }' file

1 只要这些字符是 insignificant whitespace,他们在这个例子中......

答案 2 :(得分:2)

根据您展示的样品,您可以尝试以下操作吗?在 GNU awk 中编写和测试。此外,根据专家的建议,最好使用 xml 解析工具来解析 xml 文件,因为 OP 已经在使用 awk 来解析 OP 的文件,所以继续使用它。

awk '
/^<pkg-info/ && match($0,/[[:space:]]+version="([0-9]+\.){2}[0-9]+"[[:space:]]+/){
  val=substr($0,RSTART,RLENGTH)
  gsub(/^ +| +$/,"",val)
  print val
}
' Input_file

说明:为以上添加详细说明。

awk '                             ##Starting awk program from here.
/^<pkg-info/ && match($0,/[[:space:]]+version="([0-9]+\.){2}[0-9]+"[[:space:]]+/){
                                  ##Checking condition if line starts from <pkg-info AND matches mentioned regex.
  val=substr($0,RSTART,RLENGTH)   ##Creating val which is sub string of matched regex.
  gsub(/^ +| +$/,"",val)          ##Substituting starting and ending spaces with NULL in val.
  print val                       ##Printing val value here.
}
' Input_file                      ##Mentioning Input_file name here.

答案 3 :(得分:1)

假设标签内没有换行符

gawk/mawk/mawk2 'BEGIN { FS = "version=\"" } /^[<]pkg-info/ {

    print substr($2, 1, index($2, "\"") -1 ); exit; }' 

处理随机\n的版本

gawk/mawk/mawk2 'BEGIN { FS="version=\"" } (NF > 1) { 
       
    if (seen++) { print substr($2,1,index($2, "\"")-1); exit; } }' 

这将跳过第一次看到版本,在初始 xml 标记处。第二次打印版本号然后退出。这段代码不需要对版本号的格式进行假设,除了双引号。

用于说明到处都是 pkg-info 的版本:

gawk/mawk/mawk2 'BEGIN { RS = "^$"; FS = "([<]pkg-info|[\/]pkg-info[>])";
   
   } match($2, /version=[^ ]+/) {

       print substr($2, RSTART + 9, RLENGTH - 10); exit; }'

只需在整个 XML 文件中读取它,不要尝试沿 NL 拆分内容。然后,当您强制 FS 恰好是它的开始和结束标记时,则 $2 必须是此类标记的第一次出现。

答案 4 :(得分:0)

Awk 和 XML 并不是最好的朋友,因为 awk 是一个正则表达式驱动的基于行的工具。 XML 不是一种可以使用基于行的工具轻松过滤的简单格式;因此,也很难创建一个正则表达式来可靠地匹配 XML 的所有呈现方式。

为了确保我们不会犯错,我们利用理解 XML 的状态机(过滤器)将其转换为我们可以可靠使用的基于行的内容。一个这样的工具是xml2,它从 XML 提供可解析的“平面”输出。这是您的样本过滤结果的示例....

$ xml2 < some.xml
/pkg-info/@overwrite-permissions=true
/pkg-info/@relocatable=false
/pkg-info/@identifier=com.application.something
/pkg-info/@version=1.2.3
/pkg-info/@format-version=2
/pkg-info/@generator-version=ABC
/pkg-info/@install-location=/Applications
/pkg-info/@auth=root

过滤 XML 后,创建一个可靠的 awksed 过滤器来获取我们的输出是微不足道的......这里有一些想法:

$ xml2 < some.xml | awk -F= '$1 == "/pkg-info/@version" { print $2 }'
1.2.3
$ xml2 < some.xml | sed -e 's,^/pkg-info/@version=,,; t; d'
1.2.3