[英]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 的格式可能不像问题中显示的那样(例如,所有内容都在一条长线上)。
假设:
PatientName
和StudyInstanceUID
不会出现在较大的字符串中(例如LastPatientName
或PreviousStudyInstanceUID
)PatientName
元素始终列在StudyInstanceUID
元素之前一种awk
解决方案,它消除了对echo
、 grep
和cut
的所有子流程调用的需要:
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
,您将获得下一行,但请注意,如果行按该顺序排列,则此方法有效。
awk
和sed
并非设计用于处理 XML。 请改用专用工具。 我可以推荐xidel 。
标准输出:
$ 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.