renombrar masivamente (o mostrar correctamente) archivos con caracteres especiales

20

Tengo un montón de directorios y subdirectorios que contienen archivos con caracteres especiales, como este archivo:

robbie@phil:~$ ls testsktest.txt 
test?sktest.txt

Find revela una secuencia de escape:

robbie@phil:~$ find testsktest.txt -ls 
424512 4000 -rwxr--r-x   1 robbie   robbie    4091743 Jan 26 00:34 test\323sktest.txt

La única razón por la que incluso puedo escribir sus nombres en la consola es porque se completó la pestaña. Esto también significa que puedo cambiarles el nombre manualmente (y quitarle el carácter especial).

He configurado LC_ALL en UTF-8, lo que no parece ayudar (tampoco en un nuevo shell):

robbie@phil:~$ echo $LC_ALL
en_US.UTF-8

Me estoy conectando a la máquina usando ssh desde mi mac. Es una instalación de Ubuntu:

robbie@phil:~$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=7.10
DISTRIB_CODENAME=gutsy
DISTRIB_DESCRIPTION="Ubuntu 7.10"

Shell es Bash, TERM se establece en xterm-color.

Estos archivos han estado allí durante bastante tiempo y no se han creado con esa instalación de Ubuntu. Así que no sé cuál era la configuración de codificación del sistema.

He intentado cosas como:

find . -type f -ls | sed 's/[^a-zA-Z0-9]//g'

Pero no puedo encontrar una solución que haga todo lo que quiero:

  1. Identifique todos los archivos que tienen caracteres que no se pueden mostrar (lo anterior ignora demasiado)
  2. Para todos esos archivos en un árbol de directorios (recursivamente), ejecute mv oldname newname
  3. Opcionalmente, la capacidad de transcribir caracteres especiales como ä a a (no es obligatorio, pero sería increíble)

O

  1. Muestra correctamente todos estos archivos (y no hay errores en las aplicaciones al intentar abrirlos)

Tengo partes y piezas, como iterar sobre todos los archivos y moverlos, pero identificar los archivos y formatearlos correctamente para el comando mv parece ser la parte difícil.

Cualquier información adicional sobre por qué no se muestran correctamente o cómo "adivinar" la codificación correcta también es bienvenida. (He intentado convmv pero no parece hacer exactamente lo que quiero: http://j3e.de/linux/convmv/ )

RobbieV
fuente
La única respuesta a continuación sigue la primera forma (encuéntrelos y cambie el nombre de su nueva codificación), pero la segunda forma también sería interesante: ahora, cuando conoce la codificación utilizada para los nombres de archivos remotos, cómo enviar ssh al host remoto en tal una manera de que los nombres de archivo se muestren correctamente (y se pueden administrar escribiendo sus nombres con su teclado)?
imz - Ivan Zakharyaschev

Respuestas:

21

Supongo que ves este carácter no válido porque el nombre contiene una secuencia de bytes que no es válida UTF-8. Los nombres de archivo en los sistemas de archivos Unix típicos (incluido el suyo) son cadenas de bytes, y depende de las aplicaciones decidir qué codificación usar. Hoy en día, existe una tendencia a usar UTF-8, pero no es universal, especialmente en entornos locales que nunca podrían vivir con ASCII simple y que han estado utilizando otras codificaciones desde antes de que UTF-8 existiera.

Intente LC_CTYPE=en_US.iso88591 lsver si el nombre del archivo tiene sentido en ISO-8859-1 (latin-1). Si no es así, intente con otras configuraciones regionales. Tenga en cuenta que aquí solo LC_CTYPEimporta la configuración regional.

En una configuración regional UTF-8, el siguiente comando le mostrará todos los archivos cuyo nombre no sea válido UTF-8:

grep-invalid-utf8 () {
  perl -l -ne '/^([\000-\177]|[\300-\337][\200-\277]|[\340-\357][\200-\277]{2}|[\360-\367][\200-\277]{3}|[\370-\373][\200-\277]{4}|[\374-\375][\200-\277]{5})*$/ or print'
}
find | grep-invalid-utf8

Puede verificar si tienen más sentido en otro entorno local con recode o iconv :

find | grep-invalid-utf8 | recode latin1..utf8
find | grep-invalid-utf8 | iconv -f latin1 -t utf8

Una vez que haya determinado que un grupo de nombres de archivos están en una determinada codificación (por ejemplo, latin1), una forma de cambiarles el nombre es

find | grep-invalid-utf8 |
rename 'BEGIN {binmode STDIN, ":encoding(latin1)"; use Encode;}
        $_=encode("utf8", $_)'

Utiliza el comando perl rename disponible en Debian y Ubuntu. Puede pasarlo -npara mostrar lo que estaría haciendo sin cambiar el nombre de los archivos.

Gilles 'SO- deja de ser malvado'
fuente
¡Gracias intentaré algunas de estas cosas más tarde hoy! Parece que esta será la respuesta aceptada :)
RobbieV
El hallazgo | El comando grep '[[: print:]]' parece simplemente devolver todos los archivos. ¿No debería ser UTF-8 compatible con muchas otras codificaciones con caracteres "normales"?
RobbieV
@RobbieV: Escribí y tenía la intención grep [^[:print:]]de buscar caracteres no imprimibles. Pero acabo de probar con GNU grep y no se captan secuencias UTF-8 no válidas [^[:print:]](lo que tiene sentido ya que no son caracteres no imprimibles, no son caracteres en absoluto). He editado mi publicación con una forma más larga de líneas grepping con secuencias utf8 no válidas. Tenga en cuenta que también he fijado la dirección de la recodey iconvejemplos.
Gilles 'SO- deja de ser malvado'
Eso funcionó perfectamente. Intenté todos los comandos excepto el iconv uno, y todos funcionan como se esperaba. ¡Magia pura!
RobbieV
Incluso la codificación latin1 sugerida fue la correcta :)
RobbieV
1

Sé que esta es una vieja pregunta, pero he estado buscando toda la noche una solución similar. Encontré algunos consejos útiles, pero no hicieron exactamente lo que necesitaba, así que tuve que mezclar y combinar algunos para obtener el resultado correcto que estaba buscando.

simplemente para eliminar caracteres especiales y reemplazarlos con un punto (.)

for f in *.txt; do mv "$f" `echo $f | sed "s/[^a-zA-Z0-9.]/./g"`; done

para usar en un cronjob hice lo siguiente para ejecutar cada minuto

*/1 * * * * cd /path/to/files/ && for f in *.txt; do mv "$f" `echo $f | sed "s/[^a-zA-Z0-9.]/./g"`; done >/dev/null 2>&1

Espero que alguien encuentre esto útil ya que me ha alegrado el día :)

Topps70
fuente
(1) Para mayor claridad, es posible que desee cambiar `…`a $(…): vea esto , esto y esto . (2) Siempre debe citar sus referencias de variables de shell (por ejemplo, "$f") a menos que tenga una buena razón para no hacerlo, y esté seguro de saber lo que está haciendo. Esto se aplica incluso a echo "$f" | sed …. También se aplica a toda la $(…)(o `…`) expresión; es decir, mv "$f" "$(echo "$f" | sed "…")". ... (Continúa)
Scott
(Cont.) ... (3) Debería decir , para protegerse contra los nombres de archivos que comienzan con . (4) Si tiene archivos llamados "foo ♥ bar.txt" y "foo ♠ bar.txt", esto (intentará) cambiar el nombre de ambos a "foo.bar.txt", posiblemente causando todos menos uno de los archivos a ser destruidos. (5) ¿Por qué querrías hacer esto una vez cada minuto? mv -- "$f" …-
Scott
Tengo un script de torrent que descarga automáticamente archivos. y, a veces, algunos archivos tienen caracteres que arrojan el cargador. así que simplemente cambiando el nombre de los archivos con caracteres especiales, mi cron solucionó todos mis problemas y el cargador hace su trabajo sin problemas.
Topps70
entonces (este archivo, t was - down_loaded.ext) se convierte en (this.fi.le.tha.t.was.down.loaded.ext)
Topps70
0

Ahora, cuando sepa qué codificación se usa para los nombres de archivo en el extremo remoto ("latin1", según los comentarios a la primera respuesta), también podría seguir la segunda forma : ejecute un termninal local y ssh en tal forma en que los nombres de archivos remotos se muestran correctamente (en lugar de la primera forma: renómbrelos) .

Como yo , podría iniciar un terminal localmente que funcionaría en esa codificación especial, tal vez, de esta manera:

LC_ALL = en_US.latin1 xvt &

xvt representa su programa de terminal.

Tal vez, la configuración regional existente se llama en_US.iso88591, y no en_US.latin1, como supuse.

imz - Ivan Zakharyaschev
fuente
0

Esto no cumple con los requisitos masivos, pero acabo de tener un problema similar en el que tenía varias versiones de un archivo con nombres similares que diferían solo por un solo personaje extraño. Desafortunadamente, esto significaba que no podía renombrar a los delincuentes usando el truco comodín que suelo usar.

Al final utilicé Filezilla para conectarme como un cliente SFTP, busqué los archivos y les cambié el nombre usando la GUI. Filezilla manejó los caracteres poco fiables bastante bien.

kabadisha
fuente