使用Shell命令从xml文件提取数据

时间:2019-01-04 07:14:45

标签: xml bash shell

我有一个具有以下内容的xml,我的问题是如何从资源标签提取用户名和密码值,这里我们需要使用Shell脚本排除注释的资源标签并从未注释的资源标签中获取值。我尝试过,但是它正在从最新标记中获取值。有人可以帮我如何删除注释标签和从xml中获取值。

<?xml version='1.0' encoding='utf-8'?>
<!-- The contents of this file will be loaded for each web application -->
<!--
 <Resource name="jdbcSource" auth="Container"
type="javax.sql.DataSource"
 username="demo"
    password="test"
        driverClassName="driverclassname"
        url="driver@host"
    maxActive="20"
    maxIdle="10"
     />

-->

<Resource auth="Container"
driverClassName="driverclassname" maxActive="100" maxIdle="30" maxWait="10000"
name="jdbcSource" password="test" type="javax.sql.DataSource"
url="driver@host"
username="demo"/>

</Context>

4 个答案:

答案 0 :(得分:2)

首先,我的答案假设您具有实际格式正确的源XML。您提供的示例代码不是XML,因为它没有开头的根元素,即<Context>-但我还是假设存在一个。


Bash功能本身并不十分适合解析XML。

Bash FAQ声明以下内容:

  

请勿尝试使用等[从XML文件中提取数据](以{ 3}})

如果必须使用Shell脚本,则使用XML特定的命令行工具,例如undesired results(还有其他类似的工具)。请参阅下载信息XMLStarlet-如果尚未安装XML Starlet。

解决方案:

使用XML Starlet,您可以运行以下命令:

uname=$(xml sel -t -v "/Context/Resource/@username" path/to/file.xml)
pword=$(xml sel -t -v "/Context/Resource/@password" path/to/file.xml)

echo "$uname $pword" # --> demo test

说明

  • uname=$(...)

    在这里,我们利用here将XML Startlet命令的输出分配给名为uname(即用户名)的变量。

  • xml sel -t -v "/Context/Resource/@username"

    此命令分解如下:

    • xml -调用XML Starlet命令。
    • sel -选择数据或查询XML文档。
    • -t -模板选项。
    • -v -打印XPATH表达式的值。
    • "/Context/Resource/@username" -Command substitution表达式,用于选择username标签/元素的Resource属性的值。
  • path/to/file.xml

    此部分应替换为.xml文件的真实路径。

同样,我们利用类似的命令来获取password属性的值,从而将命令的输出分配给名为pword的变量,并更改XPATH表达式。


编辑1:更有效的命令

根据下面的第一条评论...您还可以改用以下命令来更有效地提取两个属性值:

{ IFS= read -r uname && IFS= read -r pword; } < <(xml sel -t -v "/Context/Resource/@username" -n -v "/Context/Resource/@password" path/to/file.xml)

echo "$uname $pword" # --> demo test

这里的主要好处是源XML文件仅被读取一次。


编辑2:使用XML Starlet生成XSLT模板,然后可以在带有xsltproc的任何系统上运行,包括未安装XML Starlet的主机:

根据Charles Duffy在下面的第二条评论...

还可以利用XML Starlet来生成Charles Duffy模板,该模板是从先前显示的XML Starlet查询派生的。然后,可以在具有可用的任何系统上运行所生成的.xsl文件(包括未安装XML Starlet的主机)。

以下步骤演示了如何实现此目的:

  1. 首先运行以下XML Starlet命令以生成.xsl文件:

    xml sel -C -t -v "/Context/Resource/@username" -n -v "/Context/Resource/@password" path/to/file.xml > path/to/resultant/my-template.xsl
    

    此命令与先前显示的XML Starlet命令非常相似。显着的区别是:

    • -Csel之间的附加-t选项
    • 运算符>和文件路径。这指定保存输出的位置(即生成的XSLT模板/样式表)。

      注意,应根据需要更改path/to/resultant/my-template.xsl部分。

    生成的XSLT样式表的内容将类似于以下内容:

    my-template.xsl

    <?xml version="1.0"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common" version="1.0" extension-element-prefixes="exslt">
      <xsl:output omit-xml-declaration="yes" indent="no"/>
      <xsl:template match="/">
        <xsl:call-template name="value-of-template">
          <xsl:with-param name="select" select="/Context/Resource/@username"/>
        </xsl:call-template>
        <xsl:value-of select="'&#10;'"/>
        <xsl:call-template name="value-of-template">
          <xsl:with-param name="select" select="/Context/Resource/@password"/>
        </xsl:call-template>
      </xsl:template>
      <xsl:template name="value-of-template">
        <xsl:param name="select"/>
        <xsl:value-of select="$select"/>
        <xsl:for-each select="exslt:node-set($select)[position()&gt;1]">
          <xsl:value-of select="'&#10;'"/>
          <xsl:value-of select="."/>
        </xsl:for-each>
      </xsl:template>
    </xsl:stylesheet>
    
  2. 接下来,运行以下命令,该命令利用redirection转换源.xml文件。最终将转换结果分配给两个变量,即unamepword

    { IFS= read -r uname && IFS= read -r pword; } < <(xsltproc path/to/resultant/my-template.xsl path/to/file.xml)
    
    echo "$uname $pword" # --> demo test
    

    注意,应根据需要更改读取path/to/resultant/my-template.xslpath/to/file.xml的部分。


答案 1 :(得分:1)

RobC already explained为什么不应该使用本机Bash工具来解析html / xml。我建议使用专用工具,例如

我添加了<Context>as shown by m.nguyencntt开头,并将xml文件另存为so_54034541.xml

使用命令替换,您当然可以通过两次调用xidel来设置变量...

uname=$(xidel -s so_54034541.xml -e '//Resource/@username')
pword=$(xidel -s so_54034541.xml -e '//Resource/@password')

...但是xidel也有自己的导出(多个)变量的方式:

xidel -s so_54034541.xml -e '//Resource/(uname:=@username,pword:=@password)'
uname := demo   # Internal variables for use within the extraction query itself.
pword := test

xidel -s so_54034541.xml -e '//Resource/(uname:=@username,pword:=@password)' --output-format=bash
uname='demo'   # At the moment these are just strings.
pword='test'   # Use Bash's eval built-in command to actually set/export these variables.

eval "$(xidel -s so_54034541.xml -e '//Resource/(uname:=@username,pword:=@password)' --output-format=bash)"

echo "$uname $pword"
demo test

答案 2 :(得分:0)

使用perl一根内衬纸

ui

答案 3 :(得分:0)

我这样做如下:

创建了yourxmlfile.xml

<Context>
    <Resource auth="Container"
    driverClassName="driverclassname" maxActive="100" maxIdle="30" maxWait="10000"
    name="jdbcSource" password="test" type="javax.sql.DataSource"
    url="driver@host"
    username="demo"/>
</Context>

sed -n's /.[^] * password =“([[^”] )。 / \ 1 / p'yourxmlfile.xml

  test