GNU/Linux >> Tutoriales Linux >  >> Linux

Convertir todas las extensiones de archivo a minúsculas

Tuve éxito con este comando.

rename JPG jpg *.JPG

Donde rename es un comando que le dice al shell que cambie el nombre de cada aparición de JPG a jpg en la carpeta actual con todos los nombres de archivo con extensión JPG .

Si ve que Bareword "JPG" no está permitido mientras se usa "subs estrictos" en (eval 1) línea 1 con este enfoque, intente:

rename 's/\.JPG$/.jpg/' *.JPG

Solución

Puedes resolver la tarea en una línea:

find . -name '*.*' -exec sh -c '
  a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/");
  [ "$a" != "$0" ] && mv "$0" "$a" ' {} \;

Nota:esto se interrumpirá para los nombres de archivo que contengan líneas nuevas. Pero tengan paciencia conmigo por ahora.

Ejemplo de uso

$ mkdir C; touch 1.TXT a.TXT B.TXT C/D.TXT
$ find .
.
./C
./C/D.TXT
./1.TXT
./a.TXT
./B.TXT

$ find . -name '*.*' -exec sh -c 'a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/"); [ "$a" != "$0" ] && mv "$0" "$a" ' {} \;

$ find .
.
./C
./C/D.txt
./a.txt
./B.txt
./1.txt

Explicación

Encuentra todos los archivos en el directorio actual (. ) que tienen punto . en su nombre (-name '*.*' ) y ejecute el comando para cada archivo:

a=$(echo "$0" | sed -r "s/([^.]*)\$/\L\1/");
[ "$a" != "$0" ] && mv "{}" "$a"

Ese comando significa:intente convertir la extensión del archivo a minúsculas (eso hace que sed ):

$ echo 1.txt | sed -r "s/([^.]*)\$/\L\1/"
1.txt
$ echo 2.TXT | sed -r "s/([^.]*)\$/\L\1/"
2.txt

y guarde el resultado en el a variables.

Si se cambió algo [ "$a" != "$0" ] , cambie el nombre del archivo mv "$0" "$a" .

El nombre del archivo que se está procesando ({} ) pasado a sh -c como su argumento adicional y se ve dentro de la línea de comando como $0 .Hace que el script sea seguro, porque en este caso el shell toma {} como datos, no como una parte del código, como cuando se especifica directamente en la línea de comando.(Gracias @gniourf_gniourf por señalarme este tema tan importante ).

Como puede ver, si usa {} directamente en el script, es posible tener algunas inyecciones de shell en los nombres de archivo, algo como:

; rm -rf * ;

En este caso la inyección será considerada por el shell como parte del código y serán ejecutadas.

versión-While

Versión más clara, pero un poco más larga, del guión:

find . -name '*.*' | while IFS= read -r f
do
  a=$(echo "$f" | sed -r "s/([^.]*)\$/\L\1/");
  [ "$a" != "$f" ] && mv "$f" "$a"
done

Esto todavía se rompe para los nombres de archivo que contienen saltos de línea. Para solucionar este problema, debe tener un find que soporta -print0 (como GNU find ) y Bash (para que read soporta el -d interruptor delimitador):

find . -name '*.*' -print0 | while IFS= read -r -d '' f
do
  a=$(echo "$f" | sed -r "s/([^.]*)\$/\L\1/");
  [ "$a" != "$f" ] && mv "$f" "$a"
done

Esto aún falla para los archivos que contienen saltos de línea finales (ya que serán absorbidos por el a=$(...) subcapa. Si realmente desea un método infalible (¡y debería hacerlo!), con una versión reciente de Bash (Bash≥4.0) que admita ,, expansión de parámetros aquí está la solución definitiva:

find . -name '*.*' -print0 | while IFS= read -r -d '' f
do
  base=${f%.*}
  ext=${f##*.}
  a=$base.${ext,,}
  [ "$a" != "$f" ] && mv -- "$f" "$a"
done

Volver a la solución original

O en uno find go (volver a la solución original con algunas correcciones que la hacen realmente infalible):

find . -name '*.*' -type f -exec bash -c 'base=${0%.*} ext=${0##*.} a=$base.${ext,,}; [ "$a" != "$0" ] && mv -- "$0" "$a"' {} \;

Agregué -type f para que solo se cambie el nombre de los archivos normales. Sin esto, aún podría tener problemas si los nombres de los directorios se renombran antes que los nombres de los archivos. Si también desea cambiar el nombre de directorios (y enlaces, conductos, etc.), debe usar -depth :

find . -depth -name '*.*' -type f -exec bash -c 'base=${0%.*} ext=${0##*.} a=$base.${ext,,}; [ "$a" != "$0" ] && mv -- "$0" "$a"' {} \;

para que find realiza una búsqueda en profundidad.

Puede argumentar que no es eficiente generar un bash proceso para cada archivo encontrado. Eso es correcto, y la versión de bucle anterior sería mejor.


Esto es más corto pero más general, combinado con la respuesta de otros:

rename 's/\.([^.]+)$/.\L$1/' *

Simulación

Para simulación, use -n , es decir, rename -n 's/\.([^.]+)$/.\L$1/' * . De esta manera, puede ver lo que se cambiará antes de que se realicen los cambios reales. Salida de ejemplo:

Happy.Family.GATHERING.JPG renamed as Happy.Family.GATHERING.jpg
Hero_from_The_Land_Across_the_River.JPG renamed as Hero_from_The_Land_Across_the_River.jpg
rAnD0m.jPg1 renamed as rAnD0m.jpg1

Breve explicación sobre la sintaxis

  • La sintaxis es rename OPTIONS 's/WHAT_TO_FIND_IN_THE_NAME/THE_REPLACEMENT/' FILENAMES
  • \.([^.]+)$ significa secuencia de cualquier cosa menos punto ([^.] ) al final de la cadena ($ ), después del punto (\. )
  • .\L$1 significa punto (\. ) seguido de minúsculas (\L ) de 1 grupo ($1 )
  • El primer grupo en este caso es la extensión ([^.]+ )
  • Mejor usa comillas simples ' en lugar de comillas dobles " para envolver la expresión regular para evitar la expansión de shell

Linux
  1. ¿Eliminar todos los archivos/directorios excepto un archivo?

  2. ¿Escribir todo el desplazamiento hacia atrás de Tmux en un archivo?

  3. Cómo convertir archivos de texto a mayúsculas o minúsculas

  4. ¿Cómo puedo encontrar todas las extensiones de archivo distintas en una jerarquía de carpetas?

  5. ¿Cómo convertir ISO8859-15 a UTF8?

Cómo convertir un archivo de Windows a un archivo UNIX

¿Guardar toda la salida del terminal en un archivo?

enlace simbólico:encuentra todos los archivos que enlazan con este archivo

¿Escribir en un archivo .txt?

Convertir .txt a .csv en shell

formato de archivo de contraseña ldapsearch