我想将此文件中的所有撇号转换为X
:
Bob's book
Bob’s book
Bob′s book # (Might look the same but actually different)
第一个撇号按预期替换:
$ cat file | tr "'" "X"
BobXs book
Bob’s book
Bob′s book
但是另外两种撇号,会发生奇怪的事情:
$ cat file | tr "’" "X"
Bob's book
BobXXXs book
BobXX�s book
$ cat file | tr "′" "X"
Bob's book
BobXX�s book
BobXXXs book
如何让它发挥作用?
答案1
tr
以字节为单位工作,这意味着它不能正常工作于 UTF-8 等多字节编码。我所知道的唯一解决方案是找到tr
支持 Unicode 的版本,或者切换到sed
其他可以进行字符串替换的工具。
答案2
对我来说,只要您的操作系统配置为使用 utf-8 代码页,tr 就可以很好地处理 ascii 和 utf-8 文件。
这是我的示例#1(Solaris 11):
$ locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_ALL=
如您所见,操作系统配置为使用 utf-8。我用 utf-8 代码页创建了这两个文件:
$ cat file
Bob’s Bob′s Bob's
$ cat apos
’′'
然后我得到了替换所有 apos 的预期结果,如下所示:
$ cat file | tr "$(cat apos)" "xxx"
Bobxs Bobxs Bobxs
这是我的示例#2(Solaris 10):
$ locale
LANG=
LC_CTYPE="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_COLLATE="C"
LC_MONETARY="C"
LC_MESSAGES="C"
LC_ALL=
在这里您可以看到该操作系统配置为处理简单的 ASCII,而不是 utf-8,因此您可能会遇到使用 tr 处理具有多字节字符的 utf-8 文件的问题。但有解决方法。由于long tr命令允许输入字符的八进制表示,那么您可以使用八进制表示替换指定字符的所有字节。
在你的情况下,你有:
char hex octal
’ E2 80 99 \342\200\231
′ E2 80 B2 \342\200\262
' 27 \47
第一和第二个 apos 由三个字节表示。第三个是标准 ascii(一字节)。
因此,如果你想替换第一个 pos,你可以使用:
$ cat file | tr "\342\200\231" "\0\0x"
Bobxs Bob▒s Bob's
第二:
$ cat file | tr "\342\200\262" "\0\0x"
Bob▒s Bobxs Bob's
第三:
$ cat file | tr "\47" "x"
Bob’s Bob′s Bobxs
要一次性替换所有内容,您可以使用:
$ cat file | tr "\342\200\231\262\47" "\0\0xxx"
Bobxs Bobxs Bobxs
当然它并不完美,因为这将替换文件中所有出现的字节 \342、\200、\231、\262,因此包含这些字节的其他多字节字符将被破坏。但如果您的文件不包含任何其他多字节字符,它将起作用。