繁体   English   中英

bash:当要修改的项目位于grepped元素下方时,更新XML文档

[英]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($ config)并对其执行操作
  • 可能还有其他我可能要修改的与LAST_SPRINT相同值的defaultValue标记,而不是名称标记后带有值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)

...通过该脚本通过管道defaultValueSPRINT参数的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

该脚本有三个块。

  1. 如果我们找到了foundSprint并且标记为defaultValue则增加该值,当我们用“ <”或“>”分隔行时,它将在字段3 $3 然后增加该值,并打印出相应的行,以替换由于被awk视为delims而被删除的相应“ <”,“>”。 最后,将alreadyPrinted变量设置为1,这样我们就不会在步骤3中重新打印此行。
  2. 在这里,我们在标签name查找Sprint 如果找到它, foundSprint在步骤1中使用的foundSprint变量设置为1。
  3. 最后,如果我们尚未在步骤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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM