示例任务:如果一行包含foo
,则将其替换为bar
,否则追加baz
到该行。
sed -e s/foo/bar/ -e s/$/baz/
不起作用,因为无论第一个命令是否匹配,第二个命令都会执行。有没有办法告诉sed
比赛结束后转到下一行?
答案1
您可以使用t
不带标签的命令在成功替换后开始下一个周期
$ cat ip.txt
a foo 123
xyz
fore
1foo
$ sed -e 's/foo/bar/' -e t -e 's/$/baz/' ip.txt
a bar 123
xyzbaz
forebaz
1bar
来自手册:
t 标签(测试)
仅当自读取最后一个输入行或采用条件分支以来已成功替换时才分支到标签。标签可以被省略,在这种情况下开始下一个循环。
答案2
更稳健的方式awk
脚本:
awk '{ if (/foo/) gsub(/foo/, "bar"); else $0 = $0 "baz" }1' file
或者甚至更短:
awk '{ if (!gsub(/foo/, "bar")) $0 = $0 "baz"; }1' file
答案3
尝试这个:
sed -e '/foo/!s/$/baz/g' -e s/foo/bar/g
答案4
弄清楚了:
sed -e '/foo/{s//bar/;p;D};s/$/baz/'
解释:
- 多个 sed 命令可以用...链接
;
和分组。{
}
/foo/
选择包含该模式的所有行foo
。s//bar/
s/foo/bar/
与- 空模式意味着重复上次搜索(在本例中为 )相同foo
。p
表示打印模式空间(替换后的行)。D
意思是清除当前行,并转到下一行。s/$/baz/
将执行任务的第二部分,即附加baz
到前一个规则留下要通过的行。