如何从单行文本提取所有模式匹配项并输出为列表?
解决单行文本提取所有匹配模式字符串的问题
问题分析
你原来的sed命令只得到最后一个匹配项,核心原因是.*属于贪婪匹配——它会从行首一直匹配到最后一个符合${pattern}的位置,把整个单行替换成这个最后匹配的结果,自然就丢失了前面的匹配项。
快速解决方案:用grep提取所有匹配项
grep的-o选项专门用来输出每个独立的匹配内容,完美适配你的需求:
pattern="as[^',]+?\.bin" line="[['Directory of boot:/', '', '81 -rwx 110665728 17:33:44 Mar 25 2018 asa992-smp-k8.bin', '82 -rwx 32738292 17:33:44 Mar 25 2018 asdm-79247.bin', '83 -rwx 59123840 17:33:44 Mar 25 2018 asa-restapi-132300-lfbff-k8.SPA', '', '3 file(s) total size: 202527860 bytes', '534495232 bytes total (331800576 bytes free/62% free)']]" grep -oE "${pattern}" <<< "$line"
执行后会输出:
asa992-smp-k8.bin asdm-79247.bin
如果想要用空格分隔的结果,可以再加一步转换:
grep -oE "${pattern}" <<< "$line" | tr '\n' ' ' # 或者用paste实现更优雅的空格分隔 grep -oE "${pattern}" <<< "$line" | paste -sd' ' -
这样就能得到你期望的asa992-smp-k8.bin asdm-79247.bin。
用awk实现的方案
如果你更习惯用awk,也可以通过循环匹配的方式逐个提取所有结果:
awk -v pat="${pattern}" '{ while (match($0, pat, matches)) { print matches[0] $0 = substr($0, RSTART + RLENGTH) } }' <<< "$line"
这个脚本会不断在剩余的字符串里查找匹配项,直到没有匹配为止,输出结果和grep -o完全一致。
为什么原来的sed命令不行?
你的sed命令:
echo $line | sed -E "s|.* (${pattern}).*|\1|g"
这里的.* (${pattern}).*会一次性匹配整个单行文本(因为.*贪婪匹配到最后一个${pattern}的位置),然后把整个行替换成这个最后匹配的${pattern}。即使加了g选项,也只处理了一次替换(因为整个行已经被匹配完了),所以只能得到最后一个结果。
如果非要用sed实现,需要编写循环逻辑(比如不断提取第一个匹配项,然后删除已匹配的部分),但复杂度远高于grep或awk,不推荐。
内容的提问来源于stack exchange,提问作者jean-christophe manciot
相关产品推荐
相关产品推荐

