Существует следующая структура файла:
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
:h
ead (имя_каталога, как в csh/vim)(N)
:N
ullglob 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
).