¿Cómo cambio la extensión de múltiples archivos recursivamente desde la línea de comando?

61

Tengo muchos archivos con la extensión .abc y quiero cambiarlos a .edefg
¿Cómo hacer esto desde la línea de comando?

Tengo una carpeta raíz con muchas subcarpetas, por lo que la solución debería funcionar recursivamente.

    
pregunta tommyk 19.04.2011 - 14:02

8 respuestas

75

Una forma portátil (que funcionará en cualquier sistema compatible con POSIX):

find /the/path -depth -name "*.abc" -exec sh -c 'mv "" "${1%.abc}.edefg"' _ {} \;

En bash4, puedes usar globstar para obtener globs recursivos (**):

shopt -s globstar
for file in /the/path/**/*.abc; do
  mv "$file" "${file%.abc}.edefg"
done

El comando (perl) rename en Ubuntu puede cambiar el nombre de los archivos utilizando la sintaxis de expresiones regulares de Perl, que puede combinar con globstar o find :

# Using globstar
shopt -s globstar
files=(/the/path/**/*.abc)  

# Best to process the files in chunks to avoid exceeding the maximum argument 
# length. 100 at a time is probably good enough. 
# See http://mywiki.wooledge.org/BashFAQ/095
for ((i = 0; i < ${#files[@]}; i += 100)); do
  rename 's/\.abc$/.edefg/' "${files[@]:i:100}"
done

# Using find:
find /the/path -depth -name "*.abc" -exec rename 's/\.abc$/.edefg/' {} +

Vea también enlace

    
respondido por el geirha 19.04.2011 - 20:51
37

Esto hará la tarea requerida si todos los archivos están en la misma carpeta

rename 's/.abc$/.edefg/' *.abc

Para cambiar el nombre de los archivos recursivamente, usa esto:

find /path/to/root/folder -type f -name '*.abc' -print0 | xargs -0 rename 's/.abc$/.edefg/'
    
respondido por el Chakra 19.04.2011 - 14:23
11

Un problema con el cambio de nombre recursivo es que cualquiera sea el método que use para ubicar los archivos, pasa la ruta completa a rename , no solo el nombre del archivo. Eso hace que sea difícil hacer cambios de nombre complejos en carpetas anidadas.

Uso la acción find de -execdir para resolver este problema. Si usa -execdir en lugar de -exec , el comando especificado se ejecuta desde el subdirectorio que contiene el archivo coincidente. Entonces, en lugar de pasar la ruta completa a rename , solo pasa ./filename . Eso hace que sea mucho más fácil escribir la expresión regular.

find /the/path -type f \
               -name '*.abc' \
               -execdir rename 's/\.\/(.+)\.abc$/version1_.abc/' '{}' \;

En detalle:

  • -type f significa solo buscar archivos, no directorios
  • -name '*.abc' significa que significa que solo coinciden los nombres de archivo que terminan en .abc
  • Las barras invertidas después de -type y -name son el carácter de continuación de línea bash. Los uso para hacer que este ejemplo sea más legible, pero no son necesarios en la práctica.
  • Sin embargo, se requiere la barra invertida al final de la línea -execdir . Está ahí para quitar el punto y coma, que termina el comando ejecutado por -execdir . ¡Diversión!

Explicación de la expresión regular:

  • s/ start de la expresión regular
  • \.\/ coincide con el ./ que ingresa -execdir. Usa \ para escapar de. y / metacaracteres
  • (.+) coincide con el nombre del archivo. Los paréntesis capturan la coincidencia para un uso posterior
  • \.abc escapa del punto, coincide con el abc
  • $ ancla la coincidencia al final de la cadena

  • / marca el final de la parte "coincidencia" de la expresión regular y el comienzo de la parte "reemplazar"

  • version1_ agrega este texto a cada nombre de archivo

  • hace referencia al nombre de archivo existente, porque lo capturamos con paréntesis. Si usa varios conjuntos de paréntesis en la parte "coincidencia", puede consultarlos aquí usando $ 2, $ 3, etc.
  • .abc el nuevo nombre de archivo terminará en .abc. No hay necesidad de escapar del metacaracter de punto aquí en la sección "reemplazar"
  • / final de la expresión regular

Antes

tree --charset=ascii

|-- a_file.abc
|-- Another.abc
|-- Not_this.def
'-- dir1
    '-- nested_file.abc

Después de

tree --charset=ascii

|-- version1_a_file.abc
|-- version1_Another.abc
|-- Not_this.def
'-- dir1
    '-- version1_nested_file.abc

Sugerencia: la opción rename 's -n es útil. Se ejecuta en seco y le muestra los nombres que cambiará, pero no realiza ningún cambio.

    
respondido por el Christian Long 06.10.2012 - 00:48
5

Otra forma portátil:

find /the/path -depth -type f -name "*.abc" -exec sh -c 'mv -- "" "$(dirname "")/$(basename "" .abc).edefg"' _ '{}' \;
    
respondido por el aNeutrino 03.02.2013 - 17:36
0
# Rename all *.txt to *.text
for f in *.txt; do 
mv -- "$f" "${f%.txt}.text"
done

Consulte también la entrada sobre por qué no debe analizar ls .

Editar: si tiene que usar el nombre base, su sintaxis sería:

for f in *.txt; do
mv -- "$f" "$(basename "$f" .txt).text"
done

enlace

    
respondido por el ashanrupasinghe 22.08.2016 - 14:12
0

Esto es lo que hice y funcionó de la manera que yo quería. Usé el comando mv . Tenía varios archivos .3gp y quería cambiarles el nombre a .mp4

Aquí hay un delineador corto:

for i in *.3gp; do mv -- "$i" "ren-$i.mp4"; done

Que simplemente escanea a través del directorio actual, toma todos los archivos .3gp, luego cambia el nombre (usando el mv) a ren-name_of_file.mp4

    
respondido por el Rexford 13.03.2016 - 22:58
0

Encontré una manera fácil de lograr esto. Para cambiar las extensiones de muchos archivos de jpg a pdf, use:

for file in /path/to; do mv $file $(basename -s jpg $file)pdf ; done
    
respondido por el Peaceful 27.09.2017 - 05:39
0

Usaría el comando mmv del paquete del mismo nombre :

mmv ';*.abc' '#1#2.edefg'

El ; coincide con cero o más */ y corresponde a #1 en el reemplazo. El * corresponde a #2 . La versión no recursiva sería

mmv '*.abc' '#1.edefg'
    
respondido por el MvG 20.05.2018 - 23:13

Lea otras preguntas en las etiquetas