zsh: переименовать файл, если файл в подпапке имеет такое же имя

zsh: переименовать файл, если файл в подпапке имеет такое же имя

Существует следующая структура файла:

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

Я хотел бы переименовать все файлы в корневой папке, в данном случае this-file.waldoи another-file.waldo. Если в какой-либо подпапке существует файл с тем же базовым именем, что и файл .waldo, то я хотел бы переименовать файл .waldo, используя имя подпапки, содержащей одноименный файл, например:

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

Файл необходимо переименовать только в том случае, если в подпапке есть файл с таким же базовым именем, и его необходимо переименовать, дав ему то же имя, что и подпапке.

Какой наиболее эффективный способ добиться этого?

  • Всегда будет присутствовать файл .waldo.
  • Если файла с таким же базовым именем не существует, он просто игнорирует его и переходит к следующему, пока не найдет совпадение.
  • Имена файлов являются обычными именами файлов ОС, поэтому да, могут присутствовать &()[]# и т. д.

решение1

Это может быть что-то вроде:

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

(если устраивает, удалите echo(пробный запуск)).

Вот некоторые из менее обычных функций zsh, которые там можно найти:

  • for f (values) action: короткая форма цикла for (похоже на синтаксис Perl)
  • () { body; } args: анонимная функция
  • $var:r: rимя oot (часть без расширения, как в csh/vim...)
  • $var:h: head (имя_каталога, как в csh/vim)
  • (N): Nullglob glob qualifier: поэтому он не сообщает об ошибке, если совпадений нет:
  • (Y1): остановить после первого матча глобальный квалификационный турнир.

решение2

Сначала вы получаете список всех файлов в текущем каталоге.

files=(*(.DoN))

затем вы что-то делаете с каждым из них:

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

Я не стал переименовывать вас, потому что не знаю, как работать с множественными выводами, но

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

переименует fileв имя первой папки, содержащей его, и самое длинное расширение, которое у него было (если это не .waldo, но .foo.waldo, я предполагаю, что вы хотите charlie.foo.waldo, а не просто charlie.waldo).

Связанный контент