identificar archivos con caracteres no ASCII o no imprimibles en el nombre del archivo

24

En un directorio de 80 GB con aproximadamente 700,000 archivos, hay algunos nombres de archivo con caracteres que no están en inglés en el nombre del archivo. Aparte de rastrear laboriosamente la lista de archivos, hay:

  • ¿Una manera fácil de enumerar o identificar estos nombres de archivo?
  • ¿Una forma de generar caracteres imprimibles en idiomas distintos del inglés, esos caracteres que no figuran en el rango imprimible de man ascii(para que pueda probar que se están identificando estos archivos)?
sospechoso
fuente

Respuestas:

32

Suponiendo que "extraño" significa "no un carácter ASCII", puede usarlo findcon un patrón para encontrar todos los archivos que no tienen caracteres ASCII imprimibles en sus nombres:

LC_ALL=C find . -name '*[! -~]*'

(El espacio es el primer carácter imprimible que aparece en http://www.asciitable.com/ , ~es el último).

La sugerencia para LC_ALL=Ces obligatoria (en realidad, LC_CTYPE=Cy LC_COLLATE=C), de lo contrario, el rango de caracteres se interpreta incorrectamente. Vea también la página del manual glob(7). Dado que LC_ALL=Chace findque las cadenas se interpreten como ASCII, imprimirá caracteres de varios bytes (como π) como signos de interrogación. Para solucionar esto, diríjase a algún programa (por ejemplo cat) o redirija a un archivo.

En lugar de especificar rangos de caracteres, [:print:]también se puede usar para seleccionar "caracteres imprimibles". Asegúrese de establecer la configuración regional C o obtendrá un comportamiento bastante (aparentemente) arbitrario.

Ejemplo:

$ touch $(printf '\u03c0') "$(printf 'x\ty')"
$ ls -F
dir/  foo  foo.c  xrestop-0.4/  xrestop-0.4.tar.gz  π
$ find -name '*[! -~]*'       # this is broken (LC_COLLATE=en_US.UTF-8)
./x?y
./dir
./π
... (a lot more)
./foo.c
$ LC_ALL=C find . -name '*[! -~]*'
./x?y
./??
$ LC_ALL=C find . -name '*[! -~]*' | cat
./x y
./π
$ LC_ALL=C find . -name '*[![:print:]]*' | cat
./x y
./π
Lekensteyn
fuente
1
Tenga en cuenta que tiene nombres de archivo que utilizan juegos de caracteres extranjeros que son incompatibles con UTF-8 o ASCII. En esos casos, puede ver signos de interrogación en lugar de caracteres.
Lekensteyn
1
+1, pero usaría en LC_ALL=Clugar de LC_COLLATE=Chacerlo, ya que no tiene mucho sentido establecer LC_COLLATE en C sin establecerlo LC_CTYPEy asegurarme de que todavía funciona incluso cuando la variable LC_ALL está en el entorno.
Stéphane Chazelas
Si SPCes imprimible , ¿qué pasa TABy LFcuáles también se encuentran típicamente en los archivos de texto?
Stéphane Chazelas
1
Gracias, esto encontró seis archivos, que tenían guiones largos, guiones cortos y una variante de comillas simples. Todos estos se originaron de MS Word. No hay diferencia en los archivos enumerados entre LC_ALL y LC_COLLATE. LC_COLLATE muestra los caracteres no ASCII correctamente, mientras que LC_ALL muestra ??? en lugar. Excelente respuesta!
sospechoso
1
@suspectus Actualicé por respuesta en base a las sugerencias de Stephane. Para LC_COLLATEy LC_CTYPE, vea también la página de find(1)manual.
Lekensteyn
6

Si traduce cada nombre de archivo utilizando tr -d '[\200-\377]'y lo compara con el nombre original, los nombres de archivo que tengan caracteres especiales no serán los mismos.

(Lo anterior suponiendo que se refiere a no ASCII con extranjero)

Timo
fuente
2
Eso también elimina [y ]en la mayoría de las trimplementaciones.
Stéphane Chazelas
Sí, se eliminó [y ]en mi sistema.
sospechoso
+1: la solución encontró todos los (seis) nombres de archivo con símbolos no ASCII (además de los [y ]s). Gracias.
sospechoso
3

Puede usar trpara eliminar cualquier carácter extraño de un nombre de archivo y comparar el resultado con el nombre original para ver si contenía caracteres extraños.

find . -type f > filenames
while read filename; do
      stripped="$(printf '%s\n' "$filename" | tr -d -C '[[:alnum:]][[:space:]][[:punct:]]')"
      test "$filename" = "$stripped" || printf '%s\n' "$filename"; 
done < filenames
Ernest A
fuente
44
esa es una buena extensión de mi respuesta, pero es demasiado simple, los nombres de archivo pueden tener nuevas líneas y luego su script no funcionará
Timo
1
Si desea procesar la findsalida, use la salida / entrada terminada en NUL como se muestra en esta respuesta .
Lekensteyn
0

La respuesta aceptada es útil, pero si sus nombres de archivo ya están en la codificación especificada en LANG/ LC_CTYPE, es mejor simplemente hacer lo siguiente:

LC_COLLATE=C find . -name '*[! -~]*'

Las clases de caracteres se ven afectadas LC_CTYPE, pero el comando anterior no usa clases de caracteres, solo rangos, por LC_CTYPElo que evita que los caracteres inusuales sean reemplazados por signos de interrogación.

SamB
fuente