Cambie el nombre de las carpetas por lotes con un solo comando bash

Cambie el nombre de las carpetas por lotes con un solo comando bash

Tengo carpetas configuradas así:

/path/to/directory/SLUG_1/SLUG_1 - SLUG_2 - SLUG_3

SLUG_2 es ​​un año y puede tener una letra después del año, como "1994" o "2003a".

Me gustaría cambiar el nombre de esos archivos a:

/path/to/directory/SLUG_1/SLUG_2 - SLUG_3

Me estoy acercando bastante con este comando:

find $root -mindepth 2 -maxdeth 2 -type d | sed "s#\(\(/path/to/directory/[^/]*/\).* - \([0-9]\{4\}[a-bA-B]\? - .*\)\)#mv "\1" "\2\3"#

Esto imprime:

mv "/path/to/directory/SLUG_1/SLUG_1 - SLUG_2 - SLUG_3" "/path/to/directory/SLUG_1/SLUG_2 - SLUG_3"

¿Cuál es exactamente el comando que quiero ejecutar? Pero no puedo ejecutarlo.

Intenté asignar la salida a una variable y ejecutarla llamando a la variable. Eso no funcionó. Probé algunas variaciones de esa idea y obtuve errores.

Parece que me falta alguna herramienta aquí. Alguna herramienta iterativa que facilita este trabajo. ¿Alguna sugerencia?

Respuesta1

Suponiendo que todos los subdirectorios /path/to/directorysigan esa convención de nomenclatura:

cd /path/to/directory
prename -n 's~/\d{4}[a-z]? - ~/~i' */*

prenamees elcambiar el nombre de Perl(cualquiera de las variantes servirá).

Respuesta2

Esto imprime:

mv ...

¿Cuál es exactamente el comando que quiero ejecutar? Pero no puedo ejecutarlo.

Intenté asignar la salida a una variable y ejecutarla llamando a la variable. Eso no funcionó. Probé algunas variaciones de esa idea y obtuve errores.

Parece que me falta alguna herramienta aquí.

La manera fácil que estás buscandoes agregar | basha su comando. Así es como se puede pasar de "un comando que imprime un comando" a "realmente ejecutar el comando que se imprimió".

Sin embargo, aunque haya incluido comillas dobles en el comando que se imprimirá,esto es unmala ideaincluir en un guión.

Es posible que haya manejado el aspecto de los espacios en blanco, pero ¿qué pasa si los nombres de archivos incluyen comillas dobles directamente? ¿O nuevas líneas? ¿O nombres de variables (que se expandirán entre comillas dobles)?

ElsoloLos caracteres que son ilegales en los nombres de archivos son una barra diagonal ( /) y un byte nulo.

Al escribir scripts, debe cumplir con un estándar de solidez mucho más alto que en una línea de comando interactiva.

En una línea de comando, puede ver el comando que está a punto de ejecutar, puede confirmar que es correcto y luego ejecutarlo. En un guión no hay supervisión ni confirmación. El guión hará lo que usted le dijo que hiciera, sin importar cuán destructivo pueda resultar.

Entonces elcorrectoEl enfoque es realmente utilizar find, como se detalla en miotra respuesta. Esto se encargarácualquiernombres de archivos correctamente y no contiene vulnerabilidades de ejecución de código arbitrario.

Respuesta3

Otra respuesta, usar GNU findpara manejar el cambio de nombre. Este método es sólido independientemente de los caracteres que pueda haber en el nombre del archivo.

Si entiendo correctamente su caso de uso, desea cambiar el nombre de los directorios que comienzan con el nombre completo de su directorio principal. En otras palabras, si su directorio se llama así:

/some/path/abcdefghi/abcdefghi - something - else/

Quieres cambiarle el nombre así:

/some/path/abcdefghi/something - else/

Dado que especifica GNU como etiqueta en esta pregunta, puede usar las extensiones GNU para el findcomando y manejar esto de esta manera:

find . -mindepth 2 -maxdepth 2 -type d -regextype posix-egrep -regex '.*/([^/]+)/\1[^/]+' -exec sh -c 'new="$(sed -r "s:/([^/]+)/\\1 ?-? ?([^/]+)\$:/\\1/\\2:" <<<$1)"; mv "$1" "$new"' find-sh {} \;

Resultados de la prueba:

[vagrant@localhost test]$ mkdir -p SLUG_1/SLUG_{1\ -\ SLUG_{2..4}\ -\ SLUG_5,7\ -\ something}
[vagrant@localhost test]$ find . -type d
.
./SLUG_1
./SLUG_1/SLUG_1 - SLUG_2 - SLUG_5
./SLUG_1/SLUG_1 - SLUG_4 - SLUG_5
./SLUG_1/SLUG_1 - SLUG_3 - SLUG_5
./SLUG_1/SLUG_7 - something
[vagrant@localhost test]$ find . -mindepth 2 -maxdepth 2 -type d -regextype posix-egrep -regex '.*/([^/]+)/\1[^/]+' -exec sh -c 'new="$(sed -r "s:/([^/]+)/\\1 ?-? ?([^/]+)\$:/\\1/\\2:" <<<$1)"; mv "$1" "$new"' find-sh {} \;
[vagrant@localhost test]$ find . -type d
.
./SLUG_1
./SLUG_1/SLUG_3 - SLUG_5
./SLUG_1/SLUG_4 - SLUG_5
./SLUG_1/SLUG_2 - SLUG_5
./SLUG_1/SLUG_7 - something
[vagrant@localhost test]$ 

información relacionada