试图弄清楚如何将函数转换为接受管道标准输入

Trying to figure out how to convert function to accept piped stdin

我正在研究一种使用 bash 轻松解析 XML 的方法,用于特定目的。我已经将它与我在该站点上找到的一些代码一起使用,然后我重新编码了所有内容,因为该代码运行良好。这目前正在使用一个函数,我必须将数据放在一个文件中才能处理它。这是它的工作状态:

[ ~]$ cat testxml.xml
CTYPE PARTS SYSTEM "parts.dtd">
<?xml-stylesheet type="text/css" href="xmlpartsstyle.css"?>
<PARTS>
   <TITLE>Computer Parts</TITLE>
   <PART>
      <ITEM>Motherboard</ITEM>
      <MANUFACTURER>ASUS</MANUFACTURER>
      <MODEL>P3B-F</MODEL>
      <COST> 123.00</COST>
   </PART>
   <PART>
      <ITEM>Video Card</ITEM>
      <MANUFACTURER>ATI</MANUFACTURER>
      <MODEL>All-in-Wonder Pro</MODEL>
      <COST> 160.00</COST>
   </PART>
   <PART>
      <ITEM>Sound Card</ITEM>
      <MANUFACTURER>Creative Labs</MANUFACTURER>
      <MODEL>Sound Blaster Live</MODEL>
      <COST> 80.00</COST>
   </PART>
   <PART>
      <ITEM> 20 inch Monitor</ITEM>
      <MANUFACTURER>LG Electronics</MANUFACTURER>
      <MODEL> 995E</MODEL>
      <COST> 290.00</COST>
   </PART>
</PARTS>

[ ~]$
[ ~]$ rdom () { local IFS=\> ; read -d \< E C ;} ; while rdom; do if [[ $E = 'PART' ]] || [[ $E = 'ITEM' ]] || [[ $E = 'COST' ]] ; then  echo $E: $C ; fi ; done < testxml.xml | xargs -L3
PART: ITEM: Motherboard COST: 123.00
PART: ITEM: Video Card COST: 160.00
PART: ITEM: Sound Card COST: 80.00
PART: ITEM: 20 inch Monitor COST: 290.00
[ ~]$

如您所见,这提取了我正在寻找的数据,并且我能够重新格式化它以满足我的需要。但是,我更愿意让它接受来自 stdin 的输入,例如:

cat out.xml2 | IFS=\> ; until [ EOF ]; do read -d \< E C ; if [[ $E = 'PART' ]] || [[ $E = 'ITEM' ]] || [[ $E = 'COST' ]] ; then  echo $E: $C ; fi ; done;

此代码永远不会结束循环。这可能是不可能的,我只是不明白循环是如何结束的 b/c 它有 "rdom" 作为它等待显示循环终止的表达式。我已经用 while 循环等尝试过这个。不确定如何确定数据何时不再存在以便循环可以结束。我觉得可能有更好的方法来重组我完全错过的这个。我喜欢能够使用 stdin b/c 它可以轻松使用一个衬垫。我正在解析的实际数据要大得多,而且是多维的。我出于测试目的创建了这个示例。第一个示例适用于我拥有的大数据。最终结果是我试图让它从标准输入而不是从文件中解析。非常感谢任何建议。

杰夫

尝试:

$ rdom() { local IFS=\> ; while read -d \< E C ; do if [[ $E = 'PART' ]] || [[ $E = 'ITEM' ]] || [[ $E = 'COST' ]] ; then  echo $E: $C ; fi ; done; }
$ rdom <out.xml2
PART: 

ITEM: Motherboard
COST:  123.00
PART: 

ITEM: Video Card
COST:  160.00
PART: 

ITEM: Sound Card
COST:  80.00
PART: 

ITEM:  20 inch Monitor
COST:  290.00

或者,不使用函数定义但仍从标准输入获取输入:

{ IFS=\> ; while read -d \< E C ; do if [[ $E = 'PART' ]] || [[ $E = 'ITEM' ]] || [[ $E = 'COST' ]] ; then  echo $E: $C ; fi ; done; } <out.xml2

因为题目没有显示想要的输出,不知道是不是你想要的。

一些评论:

  1. cat out.xml2 | IFS=\> ;将out.xml2的文本发送给变量赋值IFS=\>。变量赋值完成后,文本被丢弃。

  2. until [ EOF ]; do read -d \< E C ; ... 没有按照您的意愿进行。在shell中,字符串EOF只有三个字符。相比之下,while read -d \< E C ; do ...会在输入耗尽时停止。

管道示例

为了证明上述方法与管道一起工作,而不仅仅是从文件重定向,请尝试:

cat out.xml2 | rdom

或者:

cat out.xml2 | { IFS=\> ; while read -d \< E C ; do if [[ $E = 'PART' ]] || [[ $E = 'ITEM' ]] || [[ $E = 'COST' ]] ; then  echo $E: $C ; fi ; done; }

替代输出格式

继续使用 cat 作为管道的替代品:

$ cat out.xml2 | { IFS=\> ; while read -d \< E C ; do case "$E" in PART) printf "%s:" "$E";; ITEM) printf " %s: %s" "$E" "$C";; COST) printf " %s: %s\n" "$E" "$C";; esac ; done; }
PART: ITEM: Motherboard COST:  123.00
PART: ITEM: Video Card COST:  160.00
PART: ITEM: Sound Card COST:  80.00
PART: ITEM:  20 inch Monitor COST:  290.00