如何使用 sed 提取子字符串
Posted
技术标签:
【中文标题】如何使用 sed 提取子字符串【英文标题】:How to use sed to extract substring 【发布时间】:2013-05-16 12:37:01 【问题描述】:我有一个包含以下行的文件:
<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
这个命令是什么?
【问题讨论】:
请注意,当 XML 在多行出现时,或者参数的顺序发生变化时,您会感到难过。如果这完全有可能,您需要考虑使用适当的 XML 解析器。 嗯,10 秒内可以回答的问题与需要更多时间的问题的双重标准?帖子在哪里询问您尝试了什么?哦等等…… 【参考方案1】:你想要awk
。
这将是一个快速而肮脏的 hack:
awk -F "\"" 'print $2' /tmp/file.txt
PortMappingEnabled
PortMappingLeaseDuration
RemoteHost
ExternalPort
ExternalPortEndRange
InternalPort
PortMappingProtocol
InternalClient
PortMappingDescription
【讨论】:
cut
会更快地完成这项工作:-)【参考方案2】:
sed 's/[^"]*"\([^"]*\).*/\1/'
完成这项工作。
''里面部分的解释
s - 告诉 sed 替换 / - 要搜索的正则表达式字符串的开始 [^"]* - 任何不是 " 的字符,任意次数。 (匹配参数名称=) " - 只是一个 "。 ([^"]*) - () 中的任何内容都将被保存以供以后参考使用。\ 在那里,因此括号不被视为要搜索的字符。[^" ]* 意思同上。 (例如匹配RemoteHost) .* - 任意字符,任意次数。 (匹配 " access="readWrite"> /parameter) / - 搜索正则表达式结束,替换字符串开始。 \1 - 引用我们在上面括号中找到的那个字符串。 /替代字符串的结尾。基本上是/搜索这个/用这个/替换,但我们告诉他用我们之前找到的一部分替换整行。
【讨论】:
这既不简单也不优雅。只是神秘。 @Stefan,也许是未经训练的眼睛。但是花点时间在 RegEx 上,喜欢爵士乐或毕加索,你会欣赏到简单的美。 这就是神秘的意思:未经训练的眼睛完全无法理解。 感谢您回答问题而不是提出不同的工具! 虽然它可以完成这项工作,但如果您能真正解释发生了什么,那将是有益的。【参考方案3】: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
【讨论】:
仅供参考,来自关于-P
的 grep 手册页:“这是高度实验性的,grep -P 可能会警告未实现的功能。”
@FukuzawaYukio 我认为ubuntu linux 自带的grep 应该支持吧?即使我不是 ubuntu 用户。这个问题被标记为 Linux 和 ubuntu,而不是 Unix 或 Aix。但你的评论是正确的。
我不得不查找\K
:它将剩余的内容保留在比赛之外(所以你不会得到name="PortMappingLeaseDuration"
。Further reading
对于那些不想使用-P
标志的人;默认 grep 支持的任何其他扩展正则表达式都不会像 \K
那样做,但您可以简单地通过 sed 管道它:grep -o 'name="[^"]* | sed 's/name="//g'
您也可以使用两次 grep:grep -o 'name="[^"]*' | grep -o '[^"]*$'
。它产生相同的结果。【参考方案4】:
您不应使用 sed 或 awk 等工具解析 XML。它很容易出错。
如果输入发生变化,并且在 name 参数之前,您将得到换行符而不是空格,它会在某天失败,产生意想不到的结果。
如果您确实确定您的输入将始终采用这种格式,您可以使用cut
。
它比sed
和awk
更快:
cut -d'"' -f2 < input.txt
最好先解析一下,只提取参数名属性:
xpath -q -e //@name input.txt | cut -d'"' -f2
要了解有关 xpath 的更多信息,请参阅本教程:http://www.w3schools.com/xpath/
【讨论】:
【参考方案5】:解释如何使用cut
:
cat yourxmlfile | cut -d'"' -f2
它将基于" delimiter'剪切'文件中的所有行,并将采用2nd field ,这就是你想要的。
【讨论】:
你想避免uselesscat
。以上是关于如何使用 sed 提取子字符串的主要内容,如果未能解决你的问题,请参考以下文章