zsh: cambie el nombre del archivo si el archivo en la subcarpeta tiene el mismo nombre

zsh: cambie el nombre del archivo si el archivo en la subcarpeta tiene el mismo nombre

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.waldoy 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: rnombre raíz (parte sin extensión, como en csh/vim...)
  • $var:h: head (nombre de directorio, como en csh/vim)
  • (N): Ncalificador 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 fileal 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).

información relacionada