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/directory
sigan esa convención de nomenclatura:
cd /path/to/directory
prename -n 's~/\d{4}[a-z]? - ~/~i' */*
prename
es 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 | bash
a 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 find
para 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 find
comando 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]$