如何在Unix中使用sed与反向引用提取子串及处理文件内容
嗨,我来一步步帮你搞定sed反向引用的问题,不管是基础用法还是你提到的具体文件处理需求,都给你讲得明明白白~
一、先搞懂sed反向引用的核心逻辑
反向引用是sed正则里的实用技巧,简单说就是:用**括号()**把你想要提取的内容“捕获”成分组,然后用\1、\2、\3...来引用这些分组的内容(\1对应第一个括号里的内容,\2`对应第二个,以此类推)。
举个简单的入门例子:假设你有一串日期字符串2024-10-05,想提取出年份和月份,改成2024年10月的格式,用sed就能这么写:
echo "2024-10-05" | sed -E 's/^([0-9]{4})-([0-9]{2})-.*/\1年\2月/'
解释下:
-E:启用扩展正则(不用给括号加反斜杠转义,写起来更舒服)^([0-9]{4}):捕获行首的4位数字(年份),作为第1个分组\1-([0-9]{2}):捕获中间的2位数字(月份),作为第2个分组\2-.*/:匹配后面的-05及所有内容,替换时直接丢弃\1年\2月:把整个字符串替换成“年份+年+月份+月”的格式
执行后输出就是:2024年10月
二、针对你的具体需求:保留数字+公司名,输出指定格式
假设你的输入文件company_list.txt内容是这样的(和你举的例子对应):
1 Walmart U.S.A.
2 SAP Germany
3 Apple Inc.
我们需要删除数字、公司名之外的所有内容,输出1.Walmart、2.SAP这种格式,用sed+反向引用可以这么写:
sed -E 's/^([0-9]+) ([A-Za-z]+)( .*)?$/\1.\2/' company_list.txt
逐部分解释这个命令:
^([0-9]+):捕获行首的一串数字(比如1、2),作为第1个分组\1([A-Za-z]+):捕获后面的公司名称(这里假设公司名是单个单词,比如Walmart、SAP),作为第2个分组\2( .*)?$:匹配行尾所有额外内容(包括空格、U.S.A.、Germany这些),这部分我们不需要,所以替换时直接忽略\1.\2:把整行内容替换成“数字+.+公司名”的格式
执行这个命令后,输出就是你想要的:
1.Walmart
2.SAP
3.Apple
进阶:如果公司名包含多个单词怎么办?
比如输入文件里有4 Amazon Web Services North America这种行,我们需要保留Amazon Web Services,去掉后面的North America,可以调整正则,把公司名的匹配规则改成允许空格:
sed -E 's/^([0-9]+) ([A-Za-z ]+)( [^A-Za-z ].*)?$/\1.\2/' company_list.txt
这里([A-Za-z ]+)允许公司名包含空格,([^A-Za-z ].*)?$用来匹配从“非字母/空格”字符开始的后续内容(如果是空格后面跟着字母,就认为是公司名的一部分,直到遇到其他字符为止)。
注意:不同系统的sed差异
如果你用的是macOS的BSD sed,-E选项是对的;如果是Linux的GNU sed,用-r或者-E都可以,效果完全一致。
内容的提问来源于stack exchange,提问作者Av1541

