[英]bash: updating an XML document when item to be modified is below grepped element
我有以下需要在bash中修改的XML(从curl输出捕获的变量中捕获)
<hudson.model.StringParameterDefinition>
<name>SPRINT</name>
<description></description>
<defaultValue>10</defaultValue>
</hudson.model.StringParameterDefinition>
我需要查找具有值SPRINT的名称标签,并递增后续的defaultValue,以便XML变为:
<hudson.model.StringParameterDefinition>
<name>SPRINT</name>
<description></description>
<defaultValue>11</defaultValue>
</hudson.model.StringParameterDefinition>
快速问题,我该怎么做?
我可以使用以下命令来实现这一点(感谢SO):
config=$(curl "$JENKINS_URL/job/$JOB_NAME/config.xml")
LAST_SPRINT=$(echo "$config" | sed -n '/<name>SPRINT<\/name>/{n;n;p}' | sed -n -e 's/.*<defaultValue>\(.*\)<\/defaultValue>.*/\1/p')
NEW_SPRINT=$((LAST_SPRINT+1))
updated_config=$(echo "$config" | sed -e "s/<defaultValue>$LAST_SPRINT<\/defaultValue>/<defaultValue>$NEW_SPRINT<\/defaultValue>/")
这是无效的,并且可能是不正确的,因为:
附带说明,在整个XML中,包含值SPRINT的名称标签只能保证出现一次。 是的,我知道bash / sed可能不是执行此操作的最佳方法,但是我仅限于SUSE Linux Enterprise Server 11上默认存在的软件包/工具。
这项工作的正确工具不是grep
,而是XML感知工具,例如XMLStarlet。
要执行您本机请求的增量一,这看起来像:
xmlstarlet ed \
-u '//hudson.model.StringParameterDefinition[./name="SPRINT"]/defaultValue' \
-x '. + 1' <in.xml >out.xml
相反,断言外壳脚本已知的值将是:
newValue=20
xmlstarlet ed \
-u '//hudson.model.StringParameterDefinition[./name="SPRINT"]/defaultValue' \
-v "$newValue" <in.xml >out.xml
以上内容已针对以下文档进行了测试,该文档仅将根元素包装在您已有的文档周围:
<root>
<hudson.model.StringParameterDefinition>
<name>SPRINT</name>
<description/>
<defaultValue>10</defaultValue>
</hudson.model.StringParameterDefinition>
</root>
如果对您的实际文档无效,则最有可能的原因是由于文档中较高级别的xmlns=
声明,这些元素不在默认名称空间中; 在询问涉及XML文档的问题时,需要包括此类声明,以提供完全响应的答案。
另外,仅使用2.7系列解释器中内置的模块的Python答案(因为您指定了无法安装第三方工具):
#!/usr/bin/env python
import sys, xml.etree.ElementTree as ET
doc = ET.fromstring(sys.stdin.read())
for node in doc.findall('.//hudson.model.StringParameterDefinition'):
name_el = node.find('./name')
if name_el is not None and name_el.text == 'SPRINT':
default_el = node.find('./defaultValue')
if default_el is None: continue
default_el.text = str(int(default_el.text) + 1)
print ET.tostring(doc)
...通过该脚本通过管道defaultValue
, SPRINT
参数的defaultValue
将增加。
将其包装在shell函数中将类似于以下内容:
# assign python code to a shell variable
_increment_sprint_script=$(cat <<'EOF'
import sys, xml.etree.ElementTree as ET
doc = ET.fromstring(sys.stdin.read())
for node in doc.findall('.//hudson.model.StringParameterDefinition'):
name_el = node.find('./name')
if name_el is not None and name_el.text == 'SPRINT':
default_el = node.find('./defaultValue')
if default_el is None: continue
default_el.text = str(int(default_el.text) + 1)
print ET.tostring(doc)
EOF
)
# define a function that calls the interpreter with that code
increment_sprint() { python -c "$_increment_sprint_script" "$@"; }
# ...then you can just pipe through it.
updated_config=$(curl "$JENKINS_URL/job/$JOB_NAME/config.xml" | increment_sprint)
上帝保佑我,这是我建造过的最丑的awk
班轮,但如果没有其他人提供更漂亮的sed
,它将做得到。 或awk
。
我相信使用正则表达式可以使您的工作更整洁,但是我担心在奇数边缘的情况下可能更容易出错。
此外,如您所述,sed和awk和grep并不是实现此目的的好工具。 从字面上看,任何可以优雅处理XML的工具都比它更受青睐。
无论如何,这是我的可憎之处:
awk -F"[<>]" 'foundSprint==1 && $2=="defaultValue" {$3=$3+1; print $1"<"$2">"$3"<"$4">";alreadyPrinted=1;foundSprint=0;} $2=="name" && $3=="SPRINT" {foundSprint=1} alreadyPrinted!=1{print $0; alreadyPrinted=0}' infile > outfile
该脚本有三个块。
foundSprint
并且标记为defaultValue
则增加该值,当我们用“ <”或“>”分隔行时,它将在字段3 $3
。 然后增加该值,并打印出相应的行,以替换由于被awk视为delims而被删除的相应“ <”,“>”。 最后,将alreadyPrinted
变量设置为1,这样我们就不会在步骤3中重新打印此行。 name
查找Sprint
。 如果找到它, foundSprint
在步骤1中使用的foundSprint
变量设置为1。 alreadyPrinted
变量重置为1以外的值。 由于标题中包含“ BASH”,因此请看一下此(纯)bash实现:)不需要其他二进制文件,它可以完成您想要的事情。
输入文件包含您给定的示例文本
享受--bvk
#!/bin/bash
FILEN=inputfile.txt
incrementit() {
local IFS="\n"
REPLACING=0
while read line; do
if [[ "$line" == *"<name>SPRINT</name>"* ]] && [[ $REPLACING == 0 ]]; then
REPLACING=1
fi
if [[ "$line" == *"<defaultValue>"*"</defaultValue>"* ]] && [[ $REPLACING == 1 ]]; then
CURVER="${line//<defaultValue>/}"
CURVER="${CURVER//<\/defaultValue>/}"
echo -e " <defaultValue>"$(( CURVER + 1 ))"</defaultValue>"
REPLACING=0
else
echo "$line"
fi
done
}
incrementit <$FILEN
考虑XSLT ,这是一种专用语言,专门用于将XML文件转换为自定义结构。 Bash可以使用xsltproc运行这样的转换:
XML (假定结构)
<root>
<hudson.model.StringParameterDefinition>
<name>SPRINT</name>
<description></description>
<defaultValue>10</defaultValue>
</hudson.model.StringParameterDefinition>
</root>
XSLT (另存为.xsl)
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates select="hudson.model.StringParameterDefinition[name='SPRINT']"/>
</xsl:copy>
</xsl:template>
<xsl:template match="hudson.model.StringParameterDefinition[name='SPRINT']">
<xsl:copy>
<xsl:copy-of select="name|description"/>
<defaultValue><xsl:value-of select="number(defaultValue) + 1"/></defaultValue>
</xsl:copy>
</xsl:template>
</xsl:transform>
重击线
shell> xsltproc transform.xsl input.xml > output.xml
产量
<root>
<hudson.model.StringParameterDefinition>
<name>SPRINT</name>
<description/>
<defaultValue>11</defaultValue>
</hudson.model.StringParameterDefinition>
</root>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.