【问题标题】:Extract specific keywords from XML file with bash script使用 bash 脚本从 XML 文件中提取特定关键字
【发布时间】:2021-03-10 05:15:32
【问题描述】:

我有一个 XML 文件,其中包含一些以特定关键词为特征的条目。 我需要在条目上运行一个 for 循环,为每个条目提取两个不同的关键字,以便它们在 for 循环中用作变量。

这是一个list.xml的例子:

<?xml version="1.0" encoding="UTF-8"?>
<responses type="C-FIND">
  <data-set xfer="1.2.840.10008.1.2.1" name="Little Endian Explicit">
    <element tag="0008,0005" vr="CS" vm="1" len="10" name="SpecificCharacterSet">ISO_IR 192</element>
    <element tag="0008,0052" vr="CS" vm="1" len="6" name="QueryRetrieveLevel">STUDY</element>
    <element tag="0008,0054" vr="AE" vm="1" len="8" name="RetrieveAETitle">PLATONE</element>
    <element tag="0010,0010" vr="PN" vm="1" len="16" name="PatientName">Anon^1600373003</element>
    <element tag="0020,000d" vr="UI" vm="1" len="42" name="StudyInstanceUID">1.3.76.13.99972.2.20181217085753.1484038.1</element>
  </data-set>
  <data-set xfer="1.2.840.10008.1.2.1" name="Little Endian Explicit">
    <element tag="0008,0005" vr="CS" vm="1" len="10" name="SpecificCharacterSet">ISO_IR 192</element>
    <element tag="0008,0052" vr="CS" vm="1" len="6" name="QueryRetrieveLevel">STUDY</element>
    <element tag="0008,0054" vr="AE" vm="1" len="8" name="RetrieveAETitle">PLATONE</element>
    <element tag="0010,0010" vr="PN" vm="1" len="16" name="PatientName">Anon^1599844862</element>
    <element tag="0020,000d" vr="UI" vm="1" len="42" name="StudyInstanceUID">1.3.76.13.99972.2.20180925142630.1456727.1</element>
  </data-set>
</responses>

我需要提取关键字“PatientName”和“StudyInstanceUID”。 我尝试使用这样的东西:

grep -A2 -i "PatientName" list.xml | while read -r string ; do
    PatientName="$(echo $string | grep -i "PatientName" | cut -d ">" -f 2 | cut -d "<" -f 1)"
    StudyInstanceUID="$(echo $string | grep -i "StudyInstanceUID" | cut -d ">" -f 2 | cut -d "<" -f 1)"
    echo "$PatientName"
    echo "$StudyInstanceUID"
done

问题是我得到了很多空行! 有什么问题?

[编辑] 我想从这个例子中得到的是这样的:

Anon^1600373003
1.3.76.13.99972.2.20181217085753.1484038.1
Anon^1599844862
1.3.76.13.99972.2.20180925142630.1456727.1

非常感谢。

伊万

【问题讨论】:

  • 使用像 xmllint 这样的专用工具
  • echo 只是一个示例,我需要在 bash 脚本中实现它以使用关键字执行其他操作。
  • @I.Iudice - 不清楚您说 “我需要提取关键字“PatientName”和“StudyInstanceUID””时的意思。你到底想达到什么目的?是给 A) 赋值吗?将Anon^1600373003Anon^1599844862 分配给PatientName 变量,同样将1.3.76.13.99972.2.20181217085753.1484038.11.3.76.13.99972.2.20180925142630.1456727.1 分配给StudyInstanceUID 变量。 B) 或者,只是将结果打印(回显)到控制台,就像您显示的那样? 3) 还是别的什么?
  • 我一直在努力实现A),我必须使用这些变量来运行信号处理算法。

标签: xml linux bash awk sed


【解决方案1】:

正如 Raman 在评论中提到的那样,使用支持 XML 的工具来解析 XML 数据可能是您最好的选择,尤其是如果您的某些 XML 的格式可能不像问题中显示的那样(例如,所有内容都在一行中) )。

假设:

  • 您可以确认您的所有数据都将按照问题中的示例进行格式化(即,每个元素都在单独的一行上)
  • 搜索字符串 PatientNameStudyInstanceUID 不会出现在较大的字符串中(例如,LastPatientNamePreviousStudyInstanceUID
  • PatientName 元素始终列在StudyInstanceUID 元素之前

一种awk 解决方案,无需对echogrepcut 进行所有子流程调用:

awk -F'[<>]' '                                    # define input field separators as "<" and ">"
/PatientName/ || /StudyInstanceUID/ { print $3 }  # if we find one of our search strings then print field #3
' list.xml

与单线相同,无 cmets:

awk -F'[<>]' '/PatientName/ || /StudyInstanceUID/ { print $3 }' list.xml

以上生成:

Anon^1600373003
1.3.76.13.99972.2.20181217085753.1484038.1
Anon^1599844862
1.3.76.13.99972.2.20180925142630.1456727.1

至于将输出捕获到变量中(例如,在while 循环中),我们可以做一些小改动,例如:

awk -F'[<>]' '
/PatientName/      { pn=$3 }                      # store field #3 in variable "pn"
/StudyInstanceUID/ { printf "%s %s\n", pn, $3 }   # print data to stdout
' list.xml

这将生成:

Anon^1600373003 1.3.76.13.99972.2.20181217085753.1484038.1
Anon^1599844862 1.3.76.13.99972.2.20180925142630.1456727.1

将其输入while 循环:

while read -r PatientName StudyInstanceUID
do
    echo "+++++++++++++++++++"
    echo "PatientName:      ${PatientName}"
    echo "StudyInstanceUID: ${StudyInstanceUID}"
done < <(awk -F'[<>]' ' /PatientName/ { pn=$3 } /StudyInstanceUID/ { printf "%s %s\n", pn, $3 } ' list.xml)

这会产生:

+++++++++++++++++++
PatientName:      Anon^1600373003
StudyInstanceUID: 1.3.76.13.99972.2.20181217085753.1484038.1
+++++++++++++++++++
PatientName:      Anon^1599844862
StudyInstanceUID: 1.3.76.13.99972.2.20180925142630.1456727.1

【讨论】:

  • 我听从了你的建议;我用 xmlstartlet 管道替换了 awk 行,它工作正常。非常感谢!
【解决方案2】:

命令:

grep -A2 -i "PatientName" list.xml

返回多行:

    <element tag="0010,0010" vr="PN" vm="1" len="16" name="PatientName">Anon^1600373003</element>
    <element tag="0020,000d" vr="UI" vm="1" len="42" name="StudyInstanceUID">1.3.76.13.99972.2.20181217085753.1484038.1</element>
  </data-set>
--
    <element tag="0010,0010" vr="PN" vm="1" len="16" name="PatientName">Anon^1599844862</element>
    <element tag="0020,000d" vr="UI" vm="1" len="42" name="StudyInstanceUID">1.3.76.13.99972.2.20180925142630.1456727.1</element>
  </data-set>

所以你的while,逐行阅读这个输出。你得到的结果是正确的,因为在线:

<element tag="0010,0010" vr="PN" vm="1" len="16" name="PatientName">Anon^1600373003</element>

StudyInstanceUID 不存在,您的变量将为空。

为了得到想要的结果,试试这个:

grep -A1 -i "PatientName" list.xml | while read -r string ; do
    PatientName="$(echo $string | grep -i "PatientName" | cut -d ">" -f 2 | cut -d "<" -f 1)"
    read string
    StudyInstanceUID="$(echo $string | grep -i "StudyInstanceUID" | cut -d ">" -f 2 | cut -d "<" -f 1)"
    echo "$PatientName"
    echo "$StudyInstanceUID"
    read string
done

使用read string,您将获得下一行,但请注意,如果行按该顺序排列,则此方法有效。

【讨论】:

    【解决方案3】:

    awksed 不是为处理 XML 而设计的。请改用专用工具。我可以推荐

    标准输出:

    $ xidel -s list.xml -e '
      //data-set/(
        element[@name="PatientName"],
        element[@name="StudyInstanceUID"]
      )
    '
    Anon^1600373003
    1.3.76.13.99972.2.20181217085753.1484038.1
    Anon^1599844862
    1.3.76.13.99972.2.20180925142630.1456727.1
    

    变量:

    $ xidel -s list.xml -e '
      //data-set/(
        eval(x"{concat("pn",position())}:=element[@name=""PatientName""]")[0],
        eval(x"{concat("si",position())}:=element[@name=""StudyInstanceUID""]")[0]
      )
    '
    pn1 := Anon^1600373003
    si1 := 1.3.76.13.99972.2.20181217085753.1484038.1
    pn2 := Anon^1599844862
    si2 := 1.3.76.13.99972.2.20180925142630.1456727.1
    

    这些是刚刚打印到标准输出的内部变量。使用--output-format=bash 和Bash 内置的eval 命令将它们转换为shell 变量。

    $ eval $(xidel -s list.xml -e '
      //data-set/(
        eval(x"{concat("pn",position())}:=element[@name=""PatientName""]")[0],
        eval(x"{concat("si",position())}:=element[@name=""StudyInstanceUID""]")[0]
      )
    ' --output-format=bash)
    
    $ printf '%s\n' $pn1 $si1 $pn2 $si2
    Anon^1600373003
    1.3.76.13.99972.2.20181217085753.1484038.1
    Anon^1599844862
    1.3.76.13.99972.2.20180925142630.1456727.1
    

    【讨论】:

      猜你喜欢
      • 2015-06-19
      • 2021-03-20
      • 2023-03-19
      • 2020-08-04
      • 2019-10-15
      • 2020-06-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多