解析 bash 脚本中从 curl 返回的 xml

解析 bash 脚本中从 curl 返回的 xml

过去几天我一直在研究 stackexchange,我发现了我想要完成的一些事情,但我不确定如何将它们组合在一起......

我正在尝试创建一个对 API 进行curl 调用的脚本。这会返回一大堆 xml,然后我想将其解析为仅某些值。总的来说,我希望这个脚本进行调用,解析值/将它们设置为变量,然后返回(显示)它们。

我可能找到了一个工作类型的解决方案,但这实用吗?

#!/bin/bash

test=$(curl -k --silent "https://username:[email protected]?.full=true&name=devicename")
test2=$(curl -k --silent "https://username:[email protected]?.devicestatus&name=devicename")

variable1=$grep -oPm1 "(?<=<name>)[^<]+" <<< "$test:)
variable2=$grep -oPm1 "(?<=<status>)[^<]+" <<< "$test:)

echo "$variable"
echo "$variable2"

[admin]>./script SwitchName UP

这是我试图挖掘的 XML:

<?xml version="1.0" ?>
<queryResponse type="AccessPointDetails" rootUrl="https://website/webacs/api/v1/data" requestUrl="https://website/webacs/api/v1/data/AccessPointDetails?.full=true&amp;name=devicename" responseType="listEntityInstances" count="1" first="0" last="0">
  <entity url="https://website/webacs/api/v1/data/AccessPointDetails/14008947223" type="AccessPointDetails" dtoType="accessPointDetailsDTO">
    <accessPointDetailsDTO id="14008947223" displayName="14008947223">
      <clientCount>6</clientCount>
      <clientCount_2_4GHz>0</clientCount_2_4GHz>
      <clientCount_5GHz>6</clientCount_5GHz>
      <ipAddress>172.16.83.5</ipAddress>
      <name>devicename</name>
      <unifiedApInfo>
        ......
      </unifiedApInfo>
      <upTime>609857</upTime>
    </accessPointDetailsDTO>
  </entity>
</queryResponse>

<?xml version="1.0" ?>
<queryResponse type="AccessPointDetails" rootUrl="https://website/webacs/api/v1/data" requestUrl="https://website/webacs/api/v1/data/AccessPointDetails?.full=true&amp;name=devicename" responseType="listEntityInstances" count="1" first="0" last="0">
  <entity url="https://website/webacs/api/v1/data/AccessPointDetails/14008947223" type="AccessPointDetails" dtoType="accessPointDetailsDTO">
    <accessPointDetailsDTO id="14008947223" displayName="14008947223">
      <name>devicename</name>
      <status>UP</status>
      <unifiedApInfo>
        ......
      </unifiedApInfo>
    </accessPointDetailsDTO>
  </entity>
</queryResponse>

答案1

首先,一些评论/问题可以让您以不同的方式思考:

(换句话说,这最初是作为评论,但后来变成了实际的答案)

  1. 当您已经知道该元素时,为什么还要尝试提取该devicename元素 - 这是您用来获取 XML 的内容(在name=devicenameURL 中)?

  2. 即使您还没有它,第二个curl命令(带有?.devicestatus)也包含devicenamestatus元素,因此您只需要获取第二个命令。

  3. 你的variable1=variable2=线路严重混乱。您在两行上都使用了$grepnot ,并且用一个而不是另一个双引号$(grep终止了双引号。:

    即它应该是这样的<<< "$test",而不是<<< "$test:

  4. 正如其他人在评论中已经提到的,使用正则表达式来解析 XML 确实不是一个好方法。请改用 XML 处理器,例如,它xmlstarlet是在 shell 脚本中处理 XML 的有用工具。或者用一种语言编写脚本(例如perlpython具有可用的 XML 处理库的语言)。在此站点和上搜索https://stackoverflow.com/对于很多例子)。

  5. 由于上面的 3. 和 4.,您的问题的答案是“不,这是不实际的,因为它根本不起作用,而且因为这里不应该使用正则表达式”。

现在提供一些可能的解决方案:

这只是修复脚本中的语法错误,以便它应该运行:

#!/bin/bash

test=$(curl -k --silent "https://username:[email protected]?.full=true&name=devicename")
test2=$(curl -k --silent "https://username:[email protected]?.devicestatus&name=devicename")

variable1=$(grep -oPm1 "(?<=<name>)[^<]+" <<< "$test1")
variable2=$(grep -oPm1 "(?<=<status>)[^<]+" <<< "$test2")

echo "$variable"
echo "$variable2"

但这远非最佳,尤其是因为正则表达式无法可靠地解析 XML。尝试这样做充其量只是一种丑陋的黑客行为,并且只有在条件(即 XML 输入)对于您尝试提取的内容绝对完美时才有效。即使服务器对 XML 输出进行很小的更改(例如消除多余的空格,包括换行符),也可能会破坏您的脚本。

如果我尝试做你正在做的事情,我大致会这样做:

#!/bin/bash

U='username'
P='password'
site='website.api.address'

element_base='queryResponse/entity'
element_AP="${element_base}/accessPointDetailsDTO"
element_status="${element_AP}/status"

devname='devicename'

url="https://${U}:${P}@${site}?.devicestatus&name=${devname}"

xml=$(curl -k --silent "$url")

status=$(printf '%s\n' "$xml" | xmlstarlet sel -t -v "$element_status")

echo "$devname: $status"

以这种方式编写脚本的有用之处之一是,通过从其他变量构建各种字符串($url特别$element_status是),可以轻松更改它们,而不会出现拼写错误或其他错误的风险。它们还可以来自命令行(例如U="$1" ; P="$2" ; devname="$3"或用于getopts处理命令行选项,如-u username -p passsword -d devicename)或来自配置文件,或两者兼而有之。您还可以devname在命令行上提供多个 s 并在循环中获取它们。

这是该脚本的另一个版本,结合了其中一些想法:

#!/bin/bash

# get username and password, and remove them from the args
U="$1" ; shift
P="$1" ; shift #edited. was $2

site='website.api.address'

element_base='queryResponse/entity'
element_AP="${element_base}/accessPointDetailsDTO"
element_status="${element_AP}/status"

url="https://${U}:${P}@${site}?.devicestatus"

for devname in "$@" ; do

  xml=$(curl -k --silent "${url}&name=${devname}")

  status=$(printf '%s\n' "$xml" | xmlstarlet sel -t -v "$element_status")

  echo "$devname: $status"
done

相关内容