我想用tr
替换字符替换字符串中的“非法”字符,其中“非法”字符全部位于一组“允许”字符之外(即它们是允许字符集的补集)。但是,当使用该-c
选项以及显式*
重复说明符或“set 2”的隐式扩展时,tr
会将替换字符的附加实例附加到输出。
重现
- 令“允许”的字符为
a-n
,按字面指定为abcdefghijklmn
。 - 令替换字符为
z
。 - 让输入字符串为
hell
或hello
。预期的输出字符串分别是 thenhell
和hellz
。
示范
存在非法字符,隐式集 2 扩展
$ echo "hello" | tr -c 'abcdefghijklmn' 'z' hellzz
预期输出是
hellz
.仅允许存在字符,隐式集 2 扩展
$ echo "hell" | tr -c 'abcdefghijklmn' 'z' hellz
预期输出是
hell
.存在非法字符,显式设置 2 扩展名
$ echo "hello" | tr -c 'abcdefghijklmn' '[z*]' hellzz
预期输出是
hellz
.只允许存在字符,显式设置 2 扩展名
$ echo "hell" | tr -c 'abcdefghijklmn' '[z*]' hellz
预期输出是
hell
.当我使用here-string而不是echo-pipe时,也会发生同样的情况(实际上,here-string是我第一次偶然发现这种效果时使用的构造):
$ tr -c 'abcdefghijkl' '[z*]' <<< "hello" hellzz
为什么这里要tr
追加一个呢?z
这是在 Linux 上,使用 bash、UTF-8 语言环境,并且tr
来自 GNU coreutils 8.25 和 8.30。
这是因为在
echo
你告诉它打印的内容的末尾添加了一个换行符。如果您使用此处字符串,情况也是如此。所以
echo "hello"
实际上打印hello\n
:这就是为什么你会看到这个:
请注意那里没有尾随换行符,并且
$
我的提示符出现在最后一个z
. 这是因为\n
末尾打印的内容hello\n
被替换为z
. 如果你使用printf
它,它会按预期工作:(
printf %s "$string"
对于任意字符串,不是)printf "$string"
或者,如果您使用
echo
支持它的,请使用echo -n
:或者,如果您有标准的 UNIX
echo
(如同时启用和选项时echo
的内置),请使用which Causes停止输出:bash
posix
xpg_echo
\c
echo
但很可能您希望在输入中保留该行分隔符,以便输出仍然是正确的文本:
(这里使用标准 POSIX 语法,而
printf
不是使用echo
它,这样可以更明显地添加换行符,并且还可以避免以字符开头-
或包含\
字符的字符串出现问题)。另请注意,根据
tr
实现的不同,它可能会留下无法单独解码为字符的字节(未更改为z
),而在其他一些(如 GNU )中tr
,它仅适用于具有单个字符的文本(以及区域设置)每个字符字节。另一种方法是使用
sed
至少在 GNU 实现中在这方面效果更好的方法:sed
作用于该行的内容,因此换行符会自动保留。