如何使用 Sed 和 Awk 在 XML 中将一个 属性 替换为另一个
How to replace one property for another in XML using Sed and Awk
我有一个包含很多 XML 个节点的文件:
<output>
<file name="user.java">
</file>
<file name="random.java">
<error line="52" column="3" severity="warning" message="User is not found." source="randomSource"/>
</file>
<output/>
现在我需要用文件中的 name
属性替换错误节点中的 source
并将其打印到文件中。所以输出文件应该有 only
行错误:
<error line="52" column="3" severity="warning" message="User is not found." name="customer.java"/>
名称最好是第一个属性:
<error name="random.java" line="52" column="3" severity="warning" message="User is not found." />
所以新文件应该只包含错误节点,我只能使用默认工具,例如 sed/awk/cut/etc...
我只知道打印错误行,但不知道如何执行上述操作:
awk -vtag=file -vp=0 '{
if([=15=]~("^<"tag)){p=1;next}
if([=15=]~("^</"tag)){p=0;printf("\n");next}
if(p==1){=;printf("%s",[=15=])}
}' infile
试试这个简单的 awk
程序:
level == 0 && [=10=] ~ "<" tag ".*>" {
print
level++
# get "name" attribute
gsub(/^.*name="/, "")
gsub(/".*$/, "")
name = [=10=]
next
}
level == 1 && /<error.*>/ {
# remove "source" attribute
gsub(/ source="[^"]*"/, "")
# put "name" attribute at the beginning of "error" tag
gsub(/<error /, "<error name=\"" name "\" ")
print
next
}
level == 1 && [=10=] ~ "</" tag ">" {
print
level--
next
}
{
print
}
这样调用:
$ cat xmlerr.xml | awk -v tag="file" -f xmlerr.awk
<output>
<file name="user.java">
</file>
<file name="random.java">
<error name="random.java" line="52" column="3" severity="warning" message="User is not found."/>
</file>
</output>
删除不必要的 print
命令
备选方案
如果你想在打开的“file
”标签中抑制“name
”属性,第一个块变成:
level == 0 && [=12=] ~ "<" tag ".*>" {
name = [=12=]
level++
n = gsub(/^.*name="/, "", name)
gsub(/".*$/, "", name)
# if substitution done, remove "name" attribute in the original line before printing
if (n > 0) {
gsub(/ name="[^"]*"/, "")
}
print
next
}
和输出:
<output>
<file>
</file>
<file>
<error name="random.java" line="52" column="3" severity="warning" message="User is not found."/>
</file>
</output>
试试这个 Perl 解决方案:
$ cat stacky.txt
<output>
<file name="user.java">
</file>
<file name="random.java">
<error line="52" column="3" severity="warning" message="User is not found." source="randomSource"/>
</file>
<output/>
$ perl -ne ' /<file (name=\S+)>/ and $x=; if(/<error/) { s/(\<error)(.*)(\bsource="[^"]+")(.+)/ $x /g ; print } ' stacky.txt
<error name="random.java" line="52" column="3" severity="warning" message="User is not found." />
假设您的输入确实如您在示例中所示那样结构化(即 <...>
s 内没有换行符,每行只有一组 <...>
s,并且全白 space 在每一行中都是空白字符)然后在每个 Unix 框上的任何 shell 中使用任何 awk 并使用以空白作为边界的文字字符串操作,因此即使文本中存在任何正则表达式或反向引用元字符,它也能正常工作,或者如果任何目标字符串都是其他字符串的子字符串:
$ cat tst.awk
{ tag=[=10=]; gsub(/^ *< *| .*$/,"",tag) }
(tag == "file") && match([=10=],/ name="[^"]+"/) {
name = substr([=10=],RSTART+1,RLENGTH-1)
}
(tag == "error") && match([=10=],/ source="[^"]+"/) {
[=10=] = substr([=10=],1,RSTART-1) substr([=10=],RSTART+RLENGTH)
match([=10=],/ *< *[^ ]+ /)
[=10=] = substr([=10=],1,RLENGTH) name substr([=10=],RSTART+RLENGTH-1)
}
{ print }
$ awk -f tst.awk file
<output>
<file name="user.java">
</file>
<file name="random.java">
<error name="random.java" line="52" column="3" severity="warning" message="User is not found."/>
</file>
<output/>
或者如果您更愿意将 source= 原位替换为 name=:
$ cat tst.awk
{ tag=[=12=]; gsub(/^ *< *| .*$/,"",tag) }
(tag == "file") && match([=12=],/ name="[^"]+"/) {
name = substr([=12=],RSTART+1,RLENGTH-1)
}
(tag == "error") && match([=12=],/ source="[^"]+"/) {
[=12=] = substr([=12=],1,RSTART) name substr([=12=],RSTART+RLENGTH)
}
{ print }
$ awk -f tst.awk file
<output>
<file name="user.java">
</file>
<file name="random.java">
<error line="52" column="3" severity="warning" message="User is not found." name="random.java"/>
</file>
<output/>
如果您只想打印“错误”行,那么在上面只需更改:
}
{ print }
至:
print
}
所以 print
只发生在 tag == "error"
块内。
我有一个包含很多 XML 个节点的文件:
<output>
<file name="user.java">
</file>
<file name="random.java">
<error line="52" column="3" severity="warning" message="User is not found." source="randomSource"/>
</file>
<output/>
现在我需要用文件中的 name
属性替换错误节点中的 source
并将其打印到文件中。所以输出文件应该有 only
行错误:
<error line="52" column="3" severity="warning" message="User is not found." name="customer.java"/>
名称最好是第一个属性:
<error name="random.java" line="52" column="3" severity="warning" message="User is not found." />
所以新文件应该只包含错误节点,我只能使用默认工具,例如 sed/awk/cut/etc...
我只知道打印错误行,但不知道如何执行上述操作:
awk -vtag=file -vp=0 '{
if([=15=]~("^<"tag)){p=1;next}
if([=15=]~("^</"tag)){p=0;printf("\n");next}
if(p==1){=;printf("%s",[=15=])}
}' infile
试试这个简单的 awk
程序:
level == 0 && [=10=] ~ "<" tag ".*>" {
print
level++
# get "name" attribute
gsub(/^.*name="/, "")
gsub(/".*$/, "")
name = [=10=]
next
}
level == 1 && /<error.*>/ {
# remove "source" attribute
gsub(/ source="[^"]*"/, "")
# put "name" attribute at the beginning of "error" tag
gsub(/<error /, "<error name=\"" name "\" ")
print
next
}
level == 1 && [=10=] ~ "</" tag ">" {
print
level--
next
}
{
print
}
这样调用:
$ cat xmlerr.xml | awk -v tag="file" -f xmlerr.awk
<output>
<file name="user.java">
</file>
<file name="random.java">
<error name="random.java" line="52" column="3" severity="warning" message="User is not found."/>
</file>
</output>
删除不必要的 print
命令
备选方案
如果你想在打开的“file
”标签中抑制“name
”属性,第一个块变成:
level == 0 && [=12=] ~ "<" tag ".*>" {
name = [=12=]
level++
n = gsub(/^.*name="/, "", name)
gsub(/".*$/, "", name)
# if substitution done, remove "name" attribute in the original line before printing
if (n > 0) {
gsub(/ name="[^"]*"/, "")
}
print
next
}
和输出:
<output>
<file>
</file>
<file>
<error name="random.java" line="52" column="3" severity="warning" message="User is not found."/>
</file>
</output>
试试这个 Perl 解决方案:
$ cat stacky.txt
<output>
<file name="user.java">
</file>
<file name="random.java">
<error line="52" column="3" severity="warning" message="User is not found." source="randomSource"/>
</file>
<output/>
$ perl -ne ' /<file (name=\S+)>/ and $x=; if(/<error/) { s/(\<error)(.*)(\bsource="[^"]+")(.+)/ $x /g ; print } ' stacky.txt
<error name="random.java" line="52" column="3" severity="warning" message="User is not found." />
假设您的输入确实如您在示例中所示那样结构化(即 <...>
s 内没有换行符,每行只有一组 <...>
s,并且全白 space 在每一行中都是空白字符)然后在每个 Unix 框上的任何 shell 中使用任何 awk 并使用以空白作为边界的文字字符串操作,因此即使文本中存在任何正则表达式或反向引用元字符,它也能正常工作,或者如果任何目标字符串都是其他字符串的子字符串:
$ cat tst.awk
{ tag=[=10=]; gsub(/^ *< *| .*$/,"",tag) }
(tag == "file") && match([=10=],/ name="[^"]+"/) {
name = substr([=10=],RSTART+1,RLENGTH-1)
}
(tag == "error") && match([=10=],/ source="[^"]+"/) {
[=10=] = substr([=10=],1,RSTART-1) substr([=10=],RSTART+RLENGTH)
match([=10=],/ *< *[^ ]+ /)
[=10=] = substr([=10=],1,RLENGTH) name substr([=10=],RSTART+RLENGTH-1)
}
{ print }
$ awk -f tst.awk file
<output>
<file name="user.java">
</file>
<file name="random.java">
<error name="random.java" line="52" column="3" severity="warning" message="User is not found."/>
</file>
<output/>
或者如果您更愿意将 source= 原位替换为 name=:
$ cat tst.awk
{ tag=[=12=]; gsub(/^ *< *| .*$/,"",tag) }
(tag == "file") && match([=12=],/ name="[^"]+"/) {
name = substr([=12=],RSTART+1,RLENGTH-1)
}
(tag == "error") && match([=12=],/ source="[^"]+"/) {
[=12=] = substr([=12=],1,RSTART) name substr([=12=],RSTART+RLENGTH)
}
{ print }
$ awk -f tst.awk file
<output>
<file name="user.java">
</file>
<file name="random.java">
<error line="52" column="3" severity="warning" message="User is not found." name="random.java"/>
</file>
<output/>
如果您只想打印“错误”行,那么在上面只需更改:
}
{ print }
至:
print
}
所以 print
只发生在 tag == "error"
块内。