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
:wq








WaS dijo:
Tio eres el amo de los juegos de caracteres, felicidades por el articulo
Maks3w dijo:
Anda, asi que UTF-8 esta basado en Unicode, pues nada a modernizarse toca.
Super Coco dijo:
@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 dijo:
¡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 dijo:
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 dijo:
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 dijo:
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?
Super Coco dijo:
@lasterra ¡Gracias! Exactamente, la conversión del contenido de dentro del fichero lo tendrás que hacer con
iconv.rsynccon--iconvsólo traduce los caracteres del nombre del fichero.patata dijo:
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
Super Coco dijo:
@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?