繁体   English   中英

使用 bash 脚本从 XML 文件中提取特定关键字

[英]Extract specific keywords from XML file with bash script

我有一个 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

非常感谢。

伊万

正如拉曼在评论中提到的那样,使用 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

与单线相同,无评论:

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

至于将 output 捕获到变量中(例如,在一个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

命令:

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 ,逐行阅读这个output。 你得到的结果是正确的,因为在线:

<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 ,您将获得下一行,但请注意,如果行按该顺序排列,则此方法有效。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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