我想使用 的sed
音译 ( y///
) 将一组字符替换为另一组字符。
我希望这与使用该程序一样有效tr
。
$ echo '[]{}abc' | tr '[ab}' 'gefh'
g]{hefc
但是,当我使用 sed 执行相同的操作时,我看到以下错误:
$ echo '[]{}abc' | sed 'y/[ab}/gefh/'
sed: 1: "y/[ab}/gefh/": unbalanced brackets ([])
这是有道理的,因为我预计需要转义该[
角色。但是,当我尝试逃避这种情况时,我收到以下不同的错误:
$ echo '[]{}abc' | sed 'y/\[ab}/gefh/'
sed: 1: "y/\[ab}/gefh/": transform strings are not the same length
我当前的解决方法是(1)仅使用tr
或(2)在音译的右侧插入一个“虚拟字符”,其作用除了匹配转义字符之外什么也不做。
$ echo '[]{}abc' | sed 'y/\[ab}/_gefh/'
g]{hefc
然而,这并不令人满意且值得怀疑。它也不是很安全,例如当\
位于输入字符串中时。
$ echo '[]{}abc\' | sed 'y/\[ab}/_gefh/'
g]{hefc_
在 sed 音译中转义字符而不将转义字符本身视为翻译的一部分的正确方法是什么?
答案1
sed
假设你使用的是 macOS(这是我可以在本机上显示此问题的唯一系统,尽管我还没有检查 macOS 的 FreeBSD 是sed
从哪里来的):
$ echo '[]{}abc' | sed 'y/[ab}/gefh/'
sed: 1: "y/[ab}/gefh/": unbalanced brackets ([])
$ echo '[]{}abc' | sed 'y/\[ab}/gefh/'
sed: 1: "y/\[ab}/gefh/": transform strings are not the same length
$ echo '[]{}abc' | sed 'y/\[ab}/\gefh/'
g]{hefc
所以,一解决办法是
- 转义
[
第一个字符串中的 以避免括号不平衡,并且 - 通过向第二个字符串添加“无操作”反斜杠,使两个字符串的长度相等。
或者,
您还可以将两个字符串都包含在 中
[...]
,经过反思,这可能是处理此问题的最安全方法,因为它可以以机械方式完成,而无需关心字符串位于字符串中的位置[
:$ echo '[]{}abc' | sed 'y/[[ab}]/[gefh]/' g]{hefc
sed
或者通过 macOS 上的 Homebrew 或 FreeBSD 的软件包系统安装 GNU ,并使用它。
我会将其视为此sed
实现中的一个错误。
答案2
您正在做的事情是正确的方法:[
始终应该是 sed 中的普通字符y///
。这与可以成为字符类(如 )的tr
一部分不同。[
[:alpha:]
不幸的是,sed 的几个实现显然有一个错误,导致它们尝试解析 sed 中的平衡括号。我已经观察到您在 FreeBSD 11.2 和 BusyBox 1.30.1 中描述的错误。
使用反斜杠很棘手:反斜杠+字符的行为不是标准除非字符是\
,n
或分隔符。因此,虽然您也许可以使用它来解决特定实现中的错误,但生成的代码可能无法与其他实现一起使用。
一个可移植的解决方法是暂时[
与您不想更改的其他字符交换,并在同一替换中转换]
为自身。为了避免可能出现错误的解析,该字符不应该是]
,^
或:
。当你进行交换时,你需要构造一个带有平衡括号并在它们之间有一些东西的字符串。 FreeBSD 也不喜欢[]
替代品;解决此问题的一个简单方法是在 之前添加一个额外的字符]
。例如,我将用作B
临时[
.
y/[B_]/B[_]/; y/Bab}/gefh/; y/[B_]/B[_]/