zsh: renomeia o arquivo se o arquivo na subpasta tiver o mesmo nome

zsh: renomeia o arquivo se o arquivo na subpasta tiver o mesmo nome

Existe a seguinte estrutura de arquivos:

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

Gostaria de renomear todos os arquivos da pasta raiz, neste caso this-file.waldoe another-file.waldo. Se em alguma subpasta existir um arquivo com o mesmo nome base de um arquivo .waldo, eu gostaria de renomear o arquivo .waldo usando o nome da subpasta que contém o homônimo da seguinte forma:

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

O arquivo deverá ser renomeado se e somente se uma subpasta tiver um arquivo que compartilhe o mesmo nome base e deverá ser renomeado com o mesmo nome da subpasta.

Qual seria a maneira mais eficiente de conseguir isso?

  • Sempre haverá um arquivo .waldo presente.
  • Se não houver nenhum arquivo que compartilhe o nome base, ele deverá simplesmente ignorar e passar para o próximo até encontrar uma correspondência.
  • Os nomes de arquivos são nomes de arquivos normais do sistema operacional, então sim, pode haver &()[]# etc.

Responder1

Isso poderia ser algo como:

for f (*.waldo(N)) (){ (($#)) && echo mv -- $f $1:h.waldo; } */$f:r.*(NY1)

(remova o echo(ensaio) se estiver satisfeito).

Alguns dos recursos menos usuais do zsh encontrados lá:

  • for f (values) action: forma abreviada do loop for (semelhante à sintaxe perl)
  • () { body; } args: função anônima
  • $var:r: root name (parte sem extensão, como em csh/vim...)
  • $var:h: head (nome do diretório, como em csh/vim)
  • (N): Nqualificador ullglob glob: para que não relate um erro se não houver correspondência:
  • (Y1): pare após a primeira partida de qualificação global.

Responder2

Primeiro, você obtém uma lista de todos os arquivos no diretório atual

files=(*(.DoN))

então, você faz algo com cada um deles:

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

Não mudei o nome para você aqui porque não tenho certeza de como lidar com várias descobertas, mas

mv -- "${file}" "${matches_in_subfolders[0]}${file#*.}"

renomearia filepara o nome da primeira pasta que o contém e a extensão mais longa que ela tinha (se não for .waldo, mas .foo.waldo, presumo que você queira charlie.foo.waldo, não apenas charlie.waldo).

informação relacionada