如何使用sed提取子字符串

87

我有一个包含以下行的文件:

  <parameter name="PortMappingEnabled" access="readWrite" type="xsd:boolean"></parameter>
  <parameter name="PortMappingLeaseDuration" access="readWrite" activeNotify="canDeny" type="xsd:unsignedInt"></parameter>
  <parameter name="RemoteHost" access="readWrite"></parameter>
  <parameter name="ExternalPort" access="readWrite" type="xsd:unsignedInt"></parameter>
  <parameter name="ExternalPortEndRange" access="readWrite" type="xsd:unsignedInt"></parameter>
  <parameter name="InternalPort" access="readWrite" type="xsd:unsignedInt"></parameter>
  <parameter name="PortMappingProtocol" access="readWrite"></parameter>
  <parameter name="InternalClient" access="readWrite"></parameter>
  <parameter name="PortMappingDescription" access="readWrite"></parameter>

我想在这个文件上执行命令,仅提取如下输出中显示的参数名称:

$sedcommand file.txt
PortMappingEnabled
PortMappingLeaseDuration
RemoteHost
ExternalPort
ExternalPortEndRange
InternalPort
PortMappingProtocol
InternalClient
PortMappingDescription

这个命令可能是什么?


1
请注意,如果XML跨越多行或参数顺序发生变化,您将会感到沮丧。如果有这种可能性,您需要考虑使用适当的XML解析器。 - Andy Lester
哦,对于那些可以在10秒钟内回答的问题和需要更多时间的问题有双重标准?那么,询问你尝试过什么的帖子在哪里呢?哦等等... - rliu
5个回答

137

grep的诞生是用于提取内容的:

grep -Po 'name="\K[^"]*'

用您的数据进行测试:

kent$  echo '<parameter name="PortMappingEnabled" access="readWrite" type="xsd:boolean"></parameter>
  <parameter name="PortMappingLeaseDuration" access="readWrite" activeNotify="canDeny" type="xsd:unsignedInt"></parameter>
  <parameter name="RemoteHost" access="readWrite"></parameter>
  <parameter name="ExternalPort" access="readWrite" type="xsd:unsignedInt"></parameter>
  <parameter name="ExternalPortEndRange" access="readWrite" type="xsd:unsignedInt"></parameter>
  <parameter name="InternalPort" access="readWrite" type="xsd:unsignedInt"></parameter>
  <parameter name="PortMappingProtocol" access="readWrite"></parameter>
  <parameter name="InternalClient" access="readWrite"></parameter>
  <parameter name="PortMappingDescription" access="readWrite"></parameter>
'|grep -Po 'name="\K[^"]*'
PortMappingEnabled
PortMappingLeaseDuration
RemoteHost
ExternalPort
ExternalPortEndRange
InternalPort
PortMappingProtocol
InternalClient
PortMappingDescription

9
提供信息,有关于-P的grep手册:“这是高度实验性的,grep-P可能会警告未实现的功能。” - Trevor Robinson
1
@FukuzawaYukio 我认为Ubuntu Linux所提供的grep应该支持它,即使我不是Ubuntu用户。问题标记为Linux和Ubuntu,而不是Unix或Aix。但是你的评论是正确的。 - Kent
12
我需要查找\K的含义:它保留了匹配项之外的剩余内容,这样您就不会得到 name="PortMappingLeaseDuration"更多阅读 - nachocab
6
对于那些不想使用-P标志的人来说,没有其他默认grep支持的扩展正则表达式可以做到\K的效果,但你可以通过将其简单地传递给sed来实现:grep -o 'name="[^"]* | sed's / name // g' - Leon S.
5
你也可以使用两次 grep 命令来实现:grep -o 'name="[^"]*' | grep -o '[^"]*$'。它会产生相同的结果。 - Crisu83
显示剩余6条评论

119

sed 's/[^"]*"\([^"]*\).*/\1/'

这段命令的作用是:

从一行文本中匹配参数名以及参数值,然后仅保留参数值。具体解释如下:

  • s - 告诉sed要进行替换操作。
  • / - 正则表达式搜索字符串开始符号。
  • [^"]* - 匹配任何非"字符的字符,出现次数任意多次(匹配parameter name=)。
  • " - 匹配一个双引号。
  • \([^"]*\) - 匹配任何不为双引号的字符,并将其保存在一个组中,以便稍后使用 (\( 和 \) 是为了避免括号被当作搜索字符)。例如,可以匹配RemoteHost参数。
  • .* - 匹配任何字符,出现次数任意多次(匹配" access="readWrite"> /parameter)。
  • / - 搜索正则表达式结束符号,同时也是替换操作开始符号。
  • \1 - 引用之前保存的组中的字符串。
  • / - 替换操作结束符号。

简单来说,就是进行类似“查找并替换”的操作,但我们要求sed仅保留先前找到的一部分内容。


68
那既不简单也不优雅,只是晦涩难懂。 - Stefan
43
也许对于未经训练的人来说,这可能是不易懂的。但是如果您花时间学习正则表达式,就像欣赏爵士乐或毕加索的艺术一样,您会欣赏到其中的简单之美。 - SaxDaddy
9
“cryptic” 的意思是:对于未经训练的人来说完全无法读懂。 - John Haugeland
54
谢谢您回答问题而不是提出其他工具! - Jason Suárez
8
虽然它能够完成工作,但如果您能够解释一下正在发生的事情将会更有益。 - Christian Herenz
显示剩余8条评论

45

你需要 awk

这是一个快速而不太严谨的方法:

awk -F "\"" '{print $2}' /tmp/file.txt

PortMappingEnabled
PortMappingLeaseDuration
RemoteHost
ExternalPort
ExternalPortEndRange
InternalPort
PortMappingProtocol
InternalClient
PortMappingDescription

3
"cut"能更快地完成工作 :-) - Michał Šrajer
awk就像核弹一样,当你只需要炸毁某些东西时使用。 - Sandburg

19

使用类似sed或awk的工具来解析XML是容易出错的。

如果输入内容发生变化,而在名称参数之前会得到换行符而不是空格,那么它将会在某一天失败并产生意外的结果。

如果您确信输入将始终以这种方式格式化,可以使用cut。它比sedawk更快:

cut -d'"' -f2 < input.txt

最好先解析它,仅提取参数名称属性:

xpath -q -e //@name input.txt | cut -d'"' -f2

如需了解更多有关xpath的信息,请参阅此教程:http://www.w3schools.com/xpath/


14

介绍如何使用 cut 命令:

cat yourxmlfile | cut -d'"' -f2

该命令将基于"作为delimiter,cut 剪切文件中的所有行,并取出第2field,这正是您需要的内容。


1
你想避免使用无用的 cat - tripleee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接