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

使用bash脚本从XML文件中提取特定关键字,xml,linux,bash,awk,sed,xidel,Xml,Linux,Bash,Awk,Sed,Xidel,我有一个XML文件,其中包含一些以特定单词为特征的条目。 我需要对条目运行for循环,为每个条目提取两个不同的关键字,以便在for循环中用作变量 $ eval $(xidel -s list.xml -e ' //data-set/( eval(x"{concat("pn",position())}:=element[@name=""PatientName""]")[0], eval(x"

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

$ 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
下面是list.xml的一个示例:


ISO_IR 192
学习
柏拉图
第160037303页
1.3.76.13.99972.2.20181217085753.1484038.1
ISO_IR 192
学习
柏拉图
Anon^1599844862
1.3.76.13.99972.2.20180925142630.1456727.1
我需要提取关键词“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
非常感谢

伊万

命令:

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>
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
grep-A1-i“PatientName”list.xml |而read-r字符串;做

PatientName=“$(echo$string | grep-i“PatientName”| cut-d“>”-f2 | cut-d“--f2 | cut-d”,正如Raman在评论中所提到的,使用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

至于将输出捕获到变量中(例如,在
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
将其送入
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)

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

Stdout:

$ 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
变量:

$ 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
这些是刚刚打印到stdout的内部变量。使用
--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

使用像XMLLinTech这样的专用工具只是一个例子,我需要在bash脚本中实现它,以使用关键字执行其他操作。@I.Iudice-当你说“我需要提取关键字”PatientName“和”StudyInstanceUID“时,你不清楚你的意思。你实际上想实现什么?是为了实现吗例如,将
Anon^1600373003
Anon^1599844862
分配给
PatientName
变量,同样地将
1.3.76.13.99972.2.20181217085753.1484038.1
1.3.76.13.99972.20180925142630.1456727.1
分配给
StudyInstanceUID
变量。B) 或者,只需将结果打印(回显)到控制台,就像您显示的那样?3) 或者别的什么?我一直在努力实现A),我必须使用这些变量来运行信号处理算法;我用Xmlet管道代替了awk管线,效果很好。谢谢!
$ 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
$ 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