我有以下 sed 表达式:
echo 'abcabcabc' | sed -n ':-A s/a/x/1; s/a/&/2;t-A; p'
据说应该用除最后一个之外的所有出现'a'
替换'x'
。所以,预期的输出是这样的:
xbcxbcabc
但实际输出是这样的:
xbcxbcxbc
将全部替换'a'
为'x'
我知道已经有类似的问题了,比如 替换每行中除最后一个字符之外的所有字符
但我在这里尝试使用 sed 条件分支的不同方法。
让我用我自己的理解来分解我的 sed 表达式
首先是 sed 表达式:
echo 'abcabcabc' | sed -n ':-A s/a/x/1; s/a/&/2;t-A; p'
sed 将其拉abcabcabc
入模式空间
然后设置一个标签:-A
然后s/a/x/1;
将第一次出现的 替换'a'
为'x'
。现在模式空间包含这个xbcabcabc
s/a/&/2;
检查模式空间是否包含两个'a'
,它确实包含两个 ,因此它'a'
用它们自己替换这两个&
。所以模式空间仍然包含这个xbcabcabc
t-A
由于最近的替换成功,跳回标签-A
从标签开始,-A
它再次执行此操作s/a/x/1;
,将模式空间的内容从 this 变为xbcabcabc
thisxbcxbcabc
s/a/&/2
它检查是否还有两个'a'
。这次模式空间包含 thisxbcxbcabc
而它没有两个,'a'
因此替换不成功。
t-A
因为最近的替换是不成功,它不应该跳回标签-A
,而是继续p
打印模式空间中的任何内容,然后xbcxbcabc
退出。但相反,即使替换是不成功它再次跳回标签-A
并将剩余的替换'a'
为'x'
。所以结果是这样的xbcxbcxbc
如果我l
在表达式之间插入:
echo 'abcabcabc' | sed -n ':-A s/a/x/1; l; s/a/&/2;t-A; p'
输出:
xbcabcabc$
xbcxbcabc$
xbcxbcxbc$
xbcxbcxbc$
xbcxbcxbc
即使模式空间包含这个,我们也可以看到它再次分支xbcxbcabc
那么,我在这里缺少什么?
答案1
请注意,它将用其自身s/a/&/2
替换第二个a
。它不会取代两个a
。同样,始终与(它将第一个替换为 )s/a/x/1
相同。这与问题无关,但仍然是一个误解,在其他情况下可能会反过来咬你一口。s/a/x/
a
x
根据 GNU手册,t
如果自读取最后一个输入行以来已成功替换,则该命令会分支,除非t
此后触发了另一个命令:sed
t label
如果自从读取最后一个输入行以及最后一个or命令s///
以来 a 已成功替换,则分支到;如果省略,则分支到脚本末尾。t
T
label
label
同一命令的 POSIX 规范同意这一点:
[2addr]t [label]
测试。分支到:
带有label
自最近读取输入行或执行 a 以来是否已进行任何替换的命令动词t
。如果label
未指定,则分支到脚本末尾。
因此,总结一下:如果任何s
命令对于单行输入成功,那么自最近的t
命令以来,该t
命令将始终分支到给定的标签。
您的数据首先转换为xbcabcabc
,然后转换为xbcxbcabc
。当得到这个结果时,s
迭代的初始命令成功地将第一个命令替换a
为x
,因此t
命令分支,给出xbcxbcxbc
。
解决此问题的一种方法是插入额外的t
命令和虚拟标签:
echo abcabcabc |
sed -e :A -e 's/a/x/' -e tB \
-e :B -e 's/a/&/2' -e tA
执行tB
第一个命令的“重置成功标志” s
。
答案2
为什么不保持简单明了并使用 awk 来代替呢?例如,使用 GNU awk 将第三个参数设置为match()
:
$ echo 'abcabcabc' |
awk '{match($0,/(.*)(a.*)/,t); gsub(/a/,"x",t[1]); print t[1] t[2]}'
xbcxbcabc
或使用任何 awk:
$ echo 'abcabcabc' |
awk '{match($0,/.*a/); t=substr($0,1,RLENGTH-1); gsub(/a/,"x",t); print t substr($0,RLENGTH)}'
xbcxbcabc
每当您发现自己考虑使用 s、g 和 p(带 -n)以外的 sed 结构时,请注意,几乎可以肯定,使用 awk 可以有一个更清晰、更简单、更高效、更健壮和/或更可移植的解决方案。
答案3
您可以反转文本,将 2 替换为 end,然后再次反转。
$ echo 'abcabcabc' | rev | sed 's/a/x/2g' | rev
xbcxbcabc
在这个简单的情况下不需要标签和循环,除非您正在做一些 sed 递归功能的练习。