sed中匹配组出现零次为何失效?如何匹配不含指定组的行?
问题:使用sed匹配不含特定路径的行并追加内容
需求:修改不包含/home/tlytle/bin路径的行,为其追加:/home/tlytle/bin。
测试文件test_file内容:
Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin #Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin Defaults secure_path = /sbin:/bin:/home/tlytle/bin:/usr/sbin:/usr/bin
尝试过程:
- 匹配包含目标路径的行并追加,命令及结果符合预期:
$ sed -E '\|/home/tlytle/bin| s|$|:/home/tlytle/bin|' test_file - 将路径分组后执行,结果仍符合预期:
$ sed -E '\|(/home/tlytle/bin)| s|$|:/home/tlytle/bin|' test_file - 匹配目标路径出现1次及以上的行,输出正常:
$ sed -E '\|(/home/tlytle/bin){1,}| s|$|:/home/tlytle/bin|' test_file - 尝试用
{0}匹配不含目标路径的行,结果所有行都被追加,不符合预期:$ sed -E '\|(/home/tlytle/bin){0}| s|$|:/home/tlytle/bin|' test_file
疑问:为何该命令无法达到预期效果?是否有其他ERE语法可实现需求?
解答
为什么{0}无法实现需求?
{0}表示匹配前面的组出现0次,但正则表达式的匹配逻辑是“只要行内存在任意位置满足条件即匹配成功”。对于任何一行,不管是否包含目标路径,行内总有空位置(比如行首、字符间隙)能让(/home/tlytle/bin){0}匹配成功,因此所有行都会被选中执行追加操作,这完全不是“不含目标路径”的逻辑。
正确实现方式
要匹配不包含特定字符串的行,sed中有两种可靠的实现思路:
方法1:使用!取反匹配(兼容性最好)
先匹配包含目标路径的行,再对不匹配的行执行替换操作:
sed -E '\|/home/tlytle/bin|! s|$|:/home/tlytle/bin|' test_file
这里的!是sed的内置取反标记,表示“对不匹配前面正则的行执行后续命令”,完全贴合需求。
方法2:使用ERE负向预查(仅GNU sed等支持)
如果一定要用ERE语法实现,可以通过负向预查确保行内不存在目标路径:
sed -E '/^(?!.*\/home\/tlytle\/bin).*$/ s|$|:/home/tlytle/bin|' test_file
这种写法依赖sed支持ERE的负向断言,GNU sed启用-E后支持,但兼容性不如方法1。
验证结果
执行方法1的命令后,输出如下(符合预期):
Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin:/home/tlytle/bin #Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin:/home/tlytle/bin Defaults secure_path = /sbin:/bin:/home/tlytle/bin:/usr/sbin:/usr/bin
前两行不含目标路径,被追加了内容;第三行保持不变。
内容的提问来源于stack exchange,提问作者T Lytle
相关产品推荐
相关产品推荐

