tr no reemplaza apóstrofo

Quiero convertir todos los apóstrofos en este file a X :

 Bob's book Bob's book Bob′s book # (Might look the same but actually different) 

El primer apóstrofo se reemplaza como se esperaba:

 $ cat file | tr "'" "X" BobXs book Bob's book Bob′s book 

Pero los otros dos types de apóstrofes suceden cosas extrañas:

 $ cat file | tr "'" "X" Bob's book BobXXXs book BobXX s book $ cat file | tr "′" "X" Bob's book BobXX s book BobXXXs book 

¿Cómo hacer que funcione?

tr funciona en unidades de bytes, lo que significa que no funciona correctamente para codificaciones multibyte como UTF-8. Las únicas soluciones que conozco son encontrar una versión de tr que admita Unicode, o cambiar a sed o alguna otra herramienta que pueda replace cadenas.

Para mí, tr funciona bien con los files ascii y utf-8 siempre que su sistema operativo esté configurado para trabajar con la página de códigos utf-8.

Aquí está mi muestra n. ° 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 puede ver, OS está configurado para trabajar con utf-8. Creé ambos files en la página de códigos utf-8:

 $ cat file Bob's Bob′s Bob's $ cat apos '′' 

Luego obtuve los resultados esperados reemplazando todos los apos como este:

 $ cat file | tr "$(cat apos)" "xxx" Bobxs Bobxs Bobxs 

Aquí está mi muestra n. ° 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= 

Aquí puede ver que este sistema operativo está configurado para manejar ASCII simple, no utf-8, por lo que puede esperar problemas al procesar files utf-8 con caracteres de varios bytes utilizando tr. Pero hay una solución. Como el command long tr permite ingresar la representación octal del carácter, entonces puede replace todos los bytes del carácter especificado usando la representación octal.

En tu caso tienes:

 char hex octal ' E2 80 99 \342\200\231 ′ E2 80 B2 \342\200\262 ' 27 \47 

Los primeros y segundos apos están representados por tres bytes. El tercero es ascii estándar (un byte).

Entonces, si quieres replace los primeros apos puedes usar:

 $ cat file | tr "\342\200\231" "\0\0x" Bobxs Bob▒s Bob's 

Segundo:

 $ cat file | tr "\342\200\262" "\0\0x" Bob▒s Bobxs Bob's 

Tercero:

 $ cat file | tr "\47" "x" Bob's Bob′s Bobxs 

Para replace todo en una sola toma, puede usar:

 $ cat file | tr "\342\200\231\262\47" "\0\0xxx" Bobxs Bobxs Bobxs 

Por supuesto, no es perfecto, siempre que esto reemplace todas las ocurrencias de byte \ 342, \ 200, \ 231, \ 262 en el file, por lo que otros caracteres de varios bytes que contengan estos bytes se romperán. Pero si su file no contiene otros caracteres de varios bytes, funcionará.