大家好,我正在尝试使用 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
提前致谢
答案 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,其中包含您希望获得的属性=值组合。
虽然您可以有一个简单的 awk
或 sed
来从您拥有的单行示例中检索 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
鉴于 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 数据的任何合法版本。此处的其他 sed
或 awk
解决方案不会。
如果您wreally wreally wreally想要使用正则表达式,Perl 是更好的选择。这适用于上述所有三个示例:
perl -0777 -lnE 'say $1 if /(?:\s|>)<pkg-info[\s\S]*?\sversion="([^"]+)"/m' file
如果你hafta hafta hafta有一个awk
,你可以设置RS-"^$"
,它的作用是将整个文件作为一个字符串读入,然后:
"<pkg-info "
找出要点。>
。但是,无论 <pkg-info
元素如何终止,都必须有一个 >
来终止它。' version="
值两侧的所有内容都用 ""
此 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 后,创建一个可靠的 awk 或 sed 过滤器来获取我们的输出是微不足道的......这里有一些想法:
$ 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