Para mim, tr funciona bem tanto para arquivos ascii quanto utf-8, desde que seu sistema operacional esteja configurado para trabalhar com a página de códigos utf-8.
Aqui está minha amostra # 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=
Como você pode ver, o sistema operacional está configurado para funcionar com o utf-8.
Eu criei ambos os arquivos na página de códigos utf-8:
$ cat file
Bob’s Bob′s Bob's
$ cat apos
’′'
Então eu tenho resultados esperados substituindo todos apos assim:
$ cat file | tr "$(cat apos)" "xxx"
Bobxs Bobxs Bobxs
Aqui está minha amostra # 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=
Aqui você pode ver que este SO está configurado para manipular ASCII simples, não utf-8, então você pode esperar problemas no processamento de arquivos utf-8 com caracteres multi-byte usando tr. Mas há uma solução alternativa. Como o comando long tr permite inserir a representação octal do caractere, então você pode substituir todos os bytes do caractere especificado usando a representação octal.
No seu caso, você tem:
char hex octal
’ E2 80 99 201
′ E2 80 B2 202
' 27
Firts e second apos são representados por três bytes. O terceiro é o padrão ascii (um byte).
Então, se você quiser substituir o primeiro apos você pode usar:
$ cat file | tr "201" "$ cat file | tr "202" "$ cat file | tr "" "x"
Bob’s Bob′s Bobxs
$ cat file | tr "2012" "$ 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=
$ cat file
Bob’s Bob′s Bob's
$ cat apos
’′'
xxx"
Bobxs Bobxs Bobxs
x"
Bob▒s Bobxs Bob's
$ cat file | tr "$(cat apos)" "xxx"
Bobxs Bobxs Bobxs
x"
Bobxs Bob▒s Bob's
Segundo:
$ locale
LANG=
LC_CTYPE="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_COLLATE="C"
LC_MONETARY="C"
LC_MESSAGES="C"
LC_ALL=
Terceiro:
char hex octal
’ E2 80 99 201
′ E2 80 B2 202
' 27
Para substituir tudo em um único tiro, você pode usar:
$ cat file | tr "201" "$ cat file | tr "202" "$ cat file | tr "" "x"
Bob’s Bob′s Bobxs
$ cat file | tr "2012" "%pre%%pre%xxx"
Bobxs Bobxs Bobxs
x"
Bob▒s Bobxs Bob's
%pre%x"
Bobxs Bob▒s Bob's
É claro que não é perfeito, desde que isso substitua todas as ocorrências de byte \ 342, \ 200, \ 231, \ 262 no arquivo, para que outros caracteres de múltiplos bytes que contenham esses bytes sejam quebrados. Mas, se o seu arquivo não contiver nenhum outro caractere multi-byte, ele funcionará.