Existe la siguiente estructura de archivos:
charlie
├──this-file.foo.1
├──this-file.foo.2
└──this-file.foo.3
foxtrot
├──another-file.foo.1
├──another-file.foo.2
├──another-file.foo.3.bar
├──another-file.foo.4.baz
└──another-file.foo.5.qux
this-file.waldo
another-file.waldo
Me gustaría cambiar el nombre de todos los archivos en la carpeta raíz, en este caso this-file.waldo
y another-file.waldo
. Si en alguna subcarpeta existe un archivo con el mismo nombre base que un archivo .waldo, entonces me gustaría cambiar el nombre del archivo .waldo usando el nombre de la subcarpeta que contiene el homónimo de esta manera:
charlie
├──this-file.foo.1
├──this-file.foo.2
└──this-file.foo.3
foxtrot
├──another-file.foo.1
├──another-file.foo.2
├──another-file.foo.3.bar
├──another-file.foo.4.baz
└──another-file.foo.5.qux
charlie.waldo
foxtrot.waldo
Se debe cambiar el nombre del archivo si y solo si una subcarpeta tiene un archivo que comparte el mismo nombre base, y se debe cambiar el nombre con el mismo nombre que la subcarpeta.
¿Cuál sería la forma más eficiente de lograr esto?
- Siempre habrá un archivo .waldo presente.
- Si no hay ningún archivo que comparta el nombre base, simplemente debe ignorarlo y pasar al siguiente hasta que encuentre una coincidencia.
- Los nombres de archivos son nombres de archivos normales del sistema operativo, por lo que sí, puede haber &()[]#, etc., presentes.
Respuesta1
Eso podría ser algo como:
for f (*.waldo(N)) (){ (($#)) && echo mv -- $f $1:h.waldo; } */$f:r.*(NY1)
(elimine el echo
(ejecución en seco) si está satisfecho).
Algunas de las características de zsh menos habituales que se encuentran allí:
for f (values) action
: forma corta del bucle for (similar a la sintaxis de Perl)() { body; } args
: función anónima$var:r
:r
nombre raíz (parte sin extensión, como en csh/vim...)$var:h
:h
ead (nombre de directorio, como en csh/vim)(N)
:N
calificador ullglob glob: por lo que no informa un error si no hay coincidencia:(Y1)
: detenerse después del primer partido de clasificación global.
Respuesta2
Primero, obtienes una lista de todos los archivos en el directorio actual.
files=(*(.DoN))
luego, le haces algo a cada uno de ellos:
for file in $files; do
# get the basename
bname="${file%%.*}"
# look for matches in subfolders
typeset -a matches_in_subfolders
matches_in_subfolders=*/${bname}*
# Check whether list of matches is longer than 0
if [ ${#matches_in_subfolders[@]} -gt 0 ] ; then
# do your magic renaming here
fi
done
No hice el cambio de nombre aquí porque no estoy seguro de cómo lidiar con múltiples hallazgos, pero
mv -- "${file}" "${matches_in_subfolders[0]}${file#*.}"
Cambiaría el nombre file
al nombre de la primera carpeta que lo contiene y a la extensión más larga que tuviera (si no es .waldo
, pero .foo.waldo
, supongo que lo desea charlie.foo.waldo
, no solo charlie.waldo
).