Bash提取某些标记之间的所有行

时间:2016-03-10 21:47:08

标签: linux bash awk sed grep

我正在尝试获取一个命令,根据xml文件中的标记数量提取多个字符串。我有这样的文件结构:

<task id="0">
some stuff
</task>

<task id="1">
some other stuff
</task>
  1. 如何获得开始和结束标记之间的所有文本?我尝试过awk和sed但没有成功。
  2. 我是否可以根据<task>标签的数量创建多个字符串?我的意思是,当我以id =“0”开始时,它会以正确的</task>标记还是文件中的最后一个标记结束?

5 个答案:

答案 0 :(得分:2)

我建议不要使用面向行的工具来处理xml内容,例如grep / sed / awk等.Xml 是面向行的格式;因此,当文本表示时,跨行的xml元素的特定分布是偶然的。 (您可以将您的示例写在一行中,但仍然是同样正确的xml格式。)

我建议在shell脚本中解析格式良好的 xml内容是xmlstarlet工具。它是一种瑞士军刀,用于以可编写脚本的方式处理xml。

首先,确保您的xml内容为well formed。以下是包含示例数据的格式良好的xml:

<?xml version="1.0" encoding="UTF-8"?>
<tasks>
<task id="0">some stuff</task>
<task id="1">some other stuff</task>
<task id="2">yet another stuff</task>
</tasks>

(可以使用xmlstarlet val检查&#34;格式良好&#34; xml文件。)

要从xml中提取内容,请使用xmlstarlet sel。此工具需要XPath表达式,用于过滤必须选择的内容。 (在大多数情况下,xmlstarlet sel和Xpath适用于xml grep和正则表达式适用于面向行的内容。)

使用保存在文件tasks.xml中的上述xml示例的示例:

提取所有任务的内容

$ xmlstarlet sel -T -t -m '/tasks/task' -v '.' -n tasks.xml 
some stuff
some other stuff
yet another stuff

获取所有任务ID

$ xmlstarlet sel -T -t -m '/tasks/task' -v '@id' -n tasks.xml 
0
1
2

提取任务0的内容

$ xmlstarlet sel -T -t -m '/tasks/task[@id="0"]' -v '.' -n tasks.xml 
some stuff

提取id大于或等于1

的所有任务的内容
$ xmlstarlet sel -T -t -m '/tasks/task[@id>="1"]' -v '.' -n tasks.xml
some other stuff
yet another stuff

天真转换为cvs格式

$ xmlstarlet sel -T -t -m '/tasks/task' -v '@id' -o ',' -v '.' -n tasks.xml 
0,some stuff
1,some other stuff
2,yet another stuff

答案 1 :(得分:1)

在GNU sed上:

sed -n '/<task id=/{n;:a;p;n;/<\/task>/!ba;s/.*/---/p;}' filename

将输出:

some stuff
---
some other stuff
---

这将搜索文件中的每个<task id=并迭代直到下一个</task>s/.*/---/p;部分将结束标记转换为分隔符,您可以将其删除并连接所有字符串。

答案 2 :(得分:1)

我为这样的事情做了HTML/XML pattern matcher

例如,您可以执行第一项任务:

$ xidel /tmp/xxx.xml -e '<task id="0">{.}</task>'
some stuff

或者对于所有任务:

$ xidel /tmp/xxx.xml -e '<task>{.}</task>+'
some stuff
some other stuff

虽然在你的情况下只有一个元素,但使用XPath更简单:

获得第一项任务:

$ xidel /tmp/xxx.xml -e //task[@id=0]
some stuff

获取所有任务内容:

$ xidel /tmp/xxx.xml -e //task
some stuff
some other stuff

答案 3 :(得分:0)

这可以通过很多方式完成。在我看来,最简单的方法是awk。把它放在一个名为task.awk的文件中:

BEGIN{x=0;}
/^<\/task>/{x=0;}
{if(x==1)print $0;}
/^<task [^>]*>/{x=1;}

然后,如果你的xml在task.xml中,你可以:

awk -f task.awk < task.xml

工作原理:

  1. 在开头将旗帜设为false。
  2. 然后首先检查我们是否应将其关闭,因为它是一个关闭标签
    • 首先执行此操作可防止打印关闭标记
  3. 然后,只有在标志位于
  4. 时才打印该行
  5. 最后检查是否应该打开它,因为它是一个开放标记
    • 最后这样做会阻止打开标签

答案 4 :(得分:0)

将此文件作为/tmp/data.xml中的来源:

<task id="0">
some1 stuff for id 0
some2 stuff for id 0
</task>

<task id="1">
some1 stuff for id 1
some2 stuff for id 1
</task>

此代码:

awk '
/<task id=/{tag_data=$0} 
/<\/task>/{tag_data=tag_data $0 " "; print tag_data} 
{tag_data=tag_data $0 " "}' < /tmp/data

产生所需的结果:

<task id="0"><task id="0"> some1 stuff for id 0 some2 stuff for id 0 </task> 
<task id="1"><task id="1"> some1 stuff for id 1 some2 stuff for id 1 </task> 

它执行以下操作: 它搜索第一个开始标记,并开始在变量tag_data中累积数据,直到它包含closinig标记。在结束标记处,您可以在tag_data变量中的开始和结束标记之间获得所有需要的数据。您可以轻松修改代码以不存储标记,甚至可以将id解析并存储在单独的变量中。