Lo hice y lo entendí

El blog de Vicente Navarro
06 jun

El paso a UTF-8 con Ubuntu Hardy Heron: rsync entre sistemas con distinto juego de caracteres

Yo suelo configurar mis sistemas Linux con el locale en_US.ISO-8859-15. Por un lado, como ya he comentado alguna vez, usando inglés es mucho más fácil buscar el significado de posibles mensajes de error y además, las traducciones no siempre son tan claras como el mensaje en inglés original. Por otro lado, el juego de caracteres ISO-8859-15 es el que en mi experiencia menos problemas da para interoperar con otros sistemas. Por ejemplo, Windows usa por defecto el juego de caracteres Windows-1252 que es un superconjunto del ISO-8859-1 y se diferencia del ISO-8859-15 sólo en unos pocos caracteres, como el símbolo del euro (€).

Así, si desde Windows hacemos un SSH con algún emulador de terminal a un sistema Linux que use ISO-8859-15 en el que exista un fichero que se llame “test-áéíóúñÑ€.txt” y no usamos ningún tipo de traslación, en el emulador de terminal veremos que el símbolo del euro ha sido reemplazado por un “¤” porque ese es el símbolo que está en la posición del euro en ISO-8859-1.

-rwx------  1 root    root         0 2007-02-11 23:36 test-áéíóúñѤ.txt*

Pero hay emuladores de terminal como el PuTTY que nos permiten elegir el juego de caracteres del sistema al que nos conectamos y traducir de dicho juego de caracteres al de Windows:

Por supuesto, elegir el juego de caracteres con el que visualizar el contenido es algo que los emuladores de terminal de Linux modernos tampoco tienen ningún problema en hacer. Es el caso, por ejemplo, del Terminal de GNOME:

Lo importante a tener en cuenta es que los nombres de los ficheros y directorios siempre quedarán escritos en el sistema de ficheros usando el juego de caracteres especificado en la variable LANG. Bueno, al menos en sistemas de ficheros nativos de Linux. En sistemas de ficheros “adoptados” como FAT o NTFS, es necesaria una traslación para que Windows no encuentre problemas.

La utilidad convmv (converts filenames from one encoding to another), disponible como paquete en Debian, permite renombrar ficheros de un juego de caracteres a otro, como en este ejemplo:

$ convmv -f iso-8859-15 -t utf-8 test-áéíóúñÑ€.txt
Starting a dry run without changes...
mv "./test-áéíóúñÑ€.txt"        "./test-áéíóúñÃâ¬.txt"
No changes to your files done. Use --notest to finally rename the files.

El texto anterior es lo que veríamos usando ISO-8859-15 como opción de traslación en PuTTY, pero si usara UTF-8, vería esto:

$ convmv -f iso-8859-15 -t utf-8 test-▒▒▒▒▒▒Ѥ.txt
Starting a dry run without changes...
mv "./test-▒▒▒▒▒▒Ѥ.txt" "./test-áéíóúñÑ€.txt"
No changes to your files done. Use --notest to finally rename the files.

pero vamos, que sólo cambia a mis ojos. Lo que ocurre en el sistema de ficheros es independiente de lo que yo veo por las conversiones que pueda -o no- hacer el emulador de terminal.

Hace poco migré uno de mis sistemas a Ubuntu Hardy Heron. Esta versión de Ubuntu, como la mayoría de distribuciones modernas usa por defecto UTF-8 (en_US.UTF-8), que es el camino a seguir para olvidarnos para siempre de problemas de interoperabilidad por el juego de caracteres, al menos entre sistemas Linux, así que he decidido olvidar mi preferencia por ISO-8859-15 y progresivamente convertir a UTF-8 todos mis sistemas.

El cambio no supone excesivos problemas. Por un lado, los escasísimos ficheros que tenía de mi Debian anterior (que estaba en ISO-8859-15) con caracteres no ASCII los he podido traducir fácilmente con convmv.

Por otro, en Cómo mantener los acentos y las eñes al montar NTFS, FAT o smbfs y al compartir directorios con Samba mostré cómo conseguir que todo funcionara perfecto usando ISO-8859-15, y la mayoría de ejemplos han sido fácilmente trasladables a UTF-8:

NTFS-3G

Usa el locale del sistema (en_US.UTF-8 en mi caso) a menos que le especifiquemos algo diferente con la opción “locale=“, de modo que sin problemas.

FAT

Si para montar FAT en un sistema UTF-8 hacemos un:

mount -t vfat -o iocharset=utf-8,codepage=850 /dev/XXX /mnt/puntodemontaje

en el dmesg nos encontraremos con este mensaje:

FAT: utf8 is not a recommended IO charset for FAT filesystems, filesystem will be case sensitive!

de modo que mejor hacemos esto:

mount -t vfat -o utf-8,codepage=850 /dev/XXX /mnt/puntodemontaje

donde la opción utf8 a secas es:

       utf8   UTF8  is  the  filesystem safe 8-bit encoding of Unicode that is
              used by the console. It can be be  enabled  for  the  filesystem
              with  this option.  If ‘uni_xlate’ gets set, UTF8 gets disabled.

ya que iocharset es una opción de “fat” (no de “vfat“) que nos explica que:

       iocharset=value
              Character set to use for converting between 8 bit characters and
              16 bit Unicode characters. The default is iso8859-1.  Long file‐
              names are stored on disk in Unicode format.

Es decir, los nombres largos en FAT se guardan en Unicode y con iocharset los trasladamos a un sistema de 8 bits como ISO-8859-15, pero para usarlos en un sistema UTF-8 lo único que tenemos que hacer es decirle que muestre el Unicode con su sistema de codificación UTF-8.

SAMBA

En la sección [global] del fichero /etc/samba/smb.conf tendríamos que poner esta vez:

dos charset = 850
unix charset = UTF8

pero en cualquier caso, UTF-8 es la opción por defecto, así que no es realmente necesario.

rsync entre sistemas con distinto juego de caracteres

Sin embargo, en esta transición a UTF-8 me he encontrado con un importante problema: el rsync.

Tal y como conté en Backups con rsync, yo uso rsync continuamente para hacer backups y para sincronizar directorios entre diferentes máquinas. Pues bien, las diferencias entre juegos de caracteres son un importante problema para hacer rsync entre un sistema Linux que use ISO-8859-15 y entre otro que use UTF-8.

Pero además, también tenemos un problema importante para hacer rsync desde un Cygwin en Windows (que usa Windows-1252) y un Linux en UTF-8, mientras que entre Cygwin y Linux en ISO-8859-1 no hay ninguno y entre Cygwin y Linux en ISO-8859-15 apenas el carácter del euro y alguno más, sin apenas importancia.

Así, hacer algo como esto entre sistemas con distinto juego de caracteres es apuesta segura para que todos los caracteres no ASCII de los nombres sean erróneos en el sistema destino:

$ rsync -av dir-áéíóúñÑ€ usuario@sistema:/tmp/
building file list ... done
dir-áéíóúñÑ€/
dir-áéíóúñÑ€/test-áéíóúñÑ€.txt

sent 5860 bytes  received 48 bytes  11816.00 bytes/sec
total size is 5720  speedup is 0.97

Para solucionar este problema la versión 3.0.0 de rsync introdujo la opción --iconv que permite especificar el juego de caracteres del sistema origen y el del sistema destino y que los caracteres se conviertan igual que haríamos con el comando iconv. De hecho, la lista de juegos de caracteres que podemos usar la tenemos que consultar con “iconv -l“.

Desfortunadamente, el rsync 3 es muy reciente, ya que fue liberado en Marzo de 2008, y aún no se ha podido incorporar a la mayoría de las distribuciones, aunque Debian ya tiene un paquete de rsync 3 para lenny/testing e incluso uno de etch-backports.

Por tanto, hasta que el rsync 3 se estandarice en las distribuciones, yo lo que voy a hacer es compilar rsync sin ejecutar “make install” para no reemplazar el paquete estándar de la distribución en los sistemas involucrados ni instalarlo en “/usr/local/“. Antes de compilar, hay que verificar que tenemos el paquete libc6-dev, que no se instala por defecto en Ubuntu, y ya podemos empezar:

~ $ wget http://samba.anu.edu.au/ftp/rsync/src/rsync-3.0.2.tar.gz

~ $ cd /usr/src/

/usr/src $ sudo tar xvfz ~/rsync-3.0.2.tar.gz

/usr/src $ cd rsync-3.0.2/

/usr/src/rsync-3.0.2 $ sudo ./configure

/usr/src/rsync-3.0.2 $ sudo make

Y ahora, para no reemplazar al rsync, lo que vamos a hacer es usar directamente el rsync recién creado en este directorio, el /usr/src/rsync-3.0.2/rsync, tanto en el origen como en el destino (por lo que usaremos la opción “--rsync-path=“). De modo que, usando el nuevo rsync de ambos extremos y estrenando la opción “--iconv=iso8859-15,utf-8” tenemos una transmisión de ficheros que ya no nos estropea los caracteres no ASCII:

$ /usr/src/rsync-3.0.2/rsync -av --iconv=iso8859-15,utf-8 \
--rsync-path=/usr/src/rsync-3.0.2/rsync dir-áéíóúñÑ€ usuario@sistema:/tmp/
sending incremental file list
dir-áéíóúñÑ€/
dir-áéíóúñÑ€/test-áéíóúñÑ€.txt

sent 5861 bytes  received 35 bytes  11792.00 bytes/sec
total size is 5720  speedup is 0.97

rsync entre Cygwin/Windows y Linux en UTF-8

Pero donde más uso rsync es para hacer backups de Windows en Linux, y en este caso, cuando un directorio tiene caracteres no ASCII, además de que los ficheros y directorios se puedan copiar con nombres raros, te puedes encontrar con errores como el siguiente si el sistema de ficheros destino está montado con NTFS-3G (para este ejemplo, en Windows he creado una carpeta llamada “dir-áéíóúñÑ” con un fichero dentro que se llama “fich-áéíñÑ.txt“):

$ rsync -av dir-áéíóúñÑ usuario@sistema:/mnt/particion_ntfs-3g/
building file list ... done
dir-\#341\#351\#355\#363\#372\#361\#321/
rsync: recv_generator: mkdir "/mnt/particion_ntfs-3g/dir-\#341\#351\#355\#363\#372\#361\#321" failed: Invalid or incomplete multibyte or wide character (84)
*** Skipping everything below this failed directory ***

sent 127 bytes  received 26 bytes  27.82 bytes/sec
total size is 0  speedup is 0.00
rsync error: some files could not be transferred (code 23) at /home/lapo/packaging/tmp/rsync-2.6.9/main.c(977) [sender=2.6.9]

En Cygwin, los caracteres no ASCII no salen bien representados en ciertas ocasiones y en su lugar sale su código en octal del juego de caracteres Windows-1252 precedido por una barra inversa (\). Por ejemplo:

á = 25510 = E116 = 3418
é = 23310 = E916 = 3518
í = 23710 = ED16 = 3558
ó = 24310 = F316 = 3638
ú = 25010 = FA16 = 3728
ñ = 24110 = F116 = 3618
Ñ = 20910 = D116 = 3218

La versión incluida actualmente de rsync en Cygwin es la 2.6.9, pero nosotros necesitamos la 3. Si instalamos el gcc, el make y todas sus dependencias en Cygwin, podremos compilar el rsync 3 exactamente igual que hemos hecho en Linux y superar el problema, esta vez usando la opción “--iconv=windows-1252,utf-8“:

$ /usr/src/rsync-3.0.2/rsync -av --iconv=windows-1252,utf-8 \
--rsync-path=/usr/src/rsync-3.0.2/rsync dir-áéíóúñÑ usuario@sistema:/mnt/particion_ntfs-3g/
sending incremental file list
dir-áéíóúñÑ/
dir-áéíóúñÑ/fich-áéíñÑ.txt

sent 152 bytes  received 35 bytes  28.77 bytes/sec
total size is 0  speedup is 0.00

Y… ¡prueba superada!

Por cierto, Cygwin no se lleva muy bien con caracteres no ASCII, como hemos visto un poco antes con los caracteres acentuados que no sacaba, sino que sacaba los códigos octales en su lugar.

Así, por ejemplo, si dentro de una shell de Cygwin escribo “find dir-” y le doy a la tecla TAB para que se autocomplete, veré que aparecen los famosos códigos en octal. Además, los caracteres no ASCII son sustituidos por un interrogante (?):

$ find dir-\341\351\355\363\372\361\321/
dir-???????/
dir-???????/fich-?????.txt

Lo podemos solucionar parcialmente poniendo lo siguiente en el fichero ~/.inputrc:

set meta-flag on 
set convert-meta off 
set output-meta on 
set input-meta on

Estas opciones nos permitirán al menos escribir acentos y eñes en el bash de Cygwin y que éste nos los muestra en las cadenas que él gestiona, pero cuando la cadena la tiene que gestionar algún otro comando, volvemos a los interrogantes:

$ find dir-áéíóúñÑ/
dir-???????/
dir-???????/fich-?????.txt

$ for i in dir*
> do
> echo $i
> done
dir-áéíóúñÑ

$ ls -d dir-áéíóúñÑ/
dir-???????/

$ ls dir-áéíóúñÑ/
fich-?????.txt

Actualización 1/1/09: La versión incluida actualmente de rsync en Cygwin es la 3.0.4, que ya incluye soporte de --iconv.

:wq

Entradas relacionadas

13 Comentarios a “El paso a UTF-8 con Ubuntu Hardy Heron: rsync entre sistemas con distinto juego de caracteres”

  • WaS dice:

    Tio eres el amo de los juegos de caracteres, felicidades por el articulo ;)

  • Maks3w dice:

    Anda, asi que UTF-8 esta basado en Unicode, pues nada a modernizarse toca.

  • @WaS ¡Gracias!

    @Maks3w Bueno, yo más bien diría que mientras que Unicode es una correspondencia entre todos los caracteres existentes y un número entero, UTF-8 es una de las diferentes formas de codificar ese número entero de forma que sea más fácilmente utilizable en las aplicaciones y sistemas operativos.

    Es decir, UTF-8 es una de las formas existentes de codificar un carácter Unicode.

  • Ringmaster dice:

    ¡Uauh! Un texto de referencia obligatoria para las dudas y problemas con los juegos de caracteres, que en linux hasta ahora me ha dado algún que otro problema (por cambiar de distro de Linspire (antes Lindows) a Mandriva (Mandrake) a Kubuntu y ahora a Ubuntu…)

  • Fjp dice:

    Impresionante.
    He leído este artículo y alguno más de los que has publicado, y pienso que rebosan calidad por todos los costados.
    Enhorabuena, un trabajo fenomenal.
    Lo de los caracteres me toca muy de cerca, ya que he estado liado estos días peleando con los charsets en java y códigos de página en dbf.

  • Iván dice:

    Muy interesante y aportando mucha información sobre algo tan “oscuro” como los juegos de caracteres. Tu artículo de samba, linux y windows me sirvió de mucho en su momento, aunque todavía no lo tengo configurado del todo bien porque si me conecto a la debian los caracteres están bien pero luego si lo miro desde windows se ven caracteres chungos. Tendré que seguir investigando.

    ¡Gracias por estos excelentes artículos!

    Saludos, Iván.

  • lasterra dice:

    El artículo es excelente. Felicidades.

    Estoy moviendo datos de un servidor con codificación ISO8859-15 a otro con UTF8 con rsync3.0.3. Por lo que veo rsync e iconv gestionan perfectamente los nombres de los ficheros si estos tienen acentos, pero por lo que veo, si el contenido del fichero tiene acentos, rsync no lo transforma a UTF8.

    Entiendo que esto queda fuera de rsync y que tendré que hacerlo directamente con iconv, verdad?

  • @lasterra ¡Gracias! Exactamente, la conversión del contenido de dentro del fichero lo tendrás que hacer con iconv. rsync con --iconv sólo traduce los caracteres del nombre del fichero.

  • patata dice:

    Hola, Super Coco

    Aunque en su momento leí este excelente artículo (me vino muy bien, para elegir qué tipo de fuente debía elegir en el Putty que uso para conectarme a mi servidor Ubuntu) hay un detalle que tuve que perfilar:

    - Cuando hacía un aptitude, veía que los cuadros de diálogo de confirmación de tareas no salían con rayas, sino con caracteres. sin embargo, en la consola del Ubuntu sí salían correctamente
    - Investigando diferencias entre las sesiones de consola y del putty, me di cuenta que el term de la consola era ‘linux’ mientras que la del putty era ‘x-windows’.
    - Así que me puse a buscar entre las opciones del Putty dónde se configuraba eso. Lo encontré, cambié la cadena…y todo ok

    Siempre gracias por tus excelentes artículos

  • @patata ¡Me alegro de que te haya ayudado! Sin embargo, ¿tal vez te refieres al artículo Juegos de caracteres: ASCII, CP850, ISO-8859-15, Unicode, UTF-8, etc. y no a éste en concreto?

  • Ramón dice:

    Al leer el comienzo de tu artículo recordé algo que me había sucedido al instalar un debian, al elegir el locale de US por las mismas razones que tú. El caso es que al imprimir ficheros pdf, estos siempre salían ligeramente empequeñecidos, y estuve algún tiempo intentando reparar lo que no estaba mal. Miré los ficheros de configuración del CUPS, investigué en internet si mi impresora tenía algún problema, en fin, todo lo que se me iba ocurriendo, pero sin resultado. Reconozco que en algunos momentos incluso llegué a dudar de debian. Pero tampoco en esa ocasión la culpa era de debian, porque lo que pasaba es que el locale de US supone que el papel es tipo letter, en vez de a4.

  • @Ramón La verdad es que a mí también me elije por defecto letter, pero yo lo cambio después a mano, así que muchas gracias por el interesante dato. Aunque es lógico que sea así, no me había parado a pensarlo…

Trackbacks y pingbacks:

Tema LHYLE09, creado por Vicente Navarro