Допустим, у меня есть куча файлов в каталоге.
filename-1.ext1
filename-1.ext2
filename-1.ext3
filename-2.ext1
filename-2.ext2
filename-2.ext3
filename-3.ext1
filename-3.ext2
filename-3.ext3
Затем я хочу создать подкаталоги в том же каталоге с тем же именем, что и файлы (имя файла, но без расширения) и переместить их следующим образом:
filename-1
├──filename-1.ext1
├──filename-1.ext2
└──filename-1.ext3
filename-2
├──filename-2.ext1
├──filename-2.ext2
└──filename-2.ext3
filename-3
├──filename-3.ext1
├──filename-3.ext2
└──filename-3.ext3
Какой наиболее эффективный способ добиться этого?
Редактировать: ext1, ext2, ext3 — это просто обозначения трех различных имен файлов, например, .foo, .bar, .top, .coffee и т. д. Не то чтобы имена файлов были похожи.
решение1
Поскольку вы используете zsh
, это можно сделать так:
for name (*.ext<->(.)) mkdir -p -- $name:r && mv -- $name $name:r
или используя более привычную длинную форму цикла for
,
for name in *.ext<->(.); do
mkdir -p -- $name:r &&
mv -- $name $name:r
done
Это перебирает все обычные файлы, соответствующие шаблону, *.ext<->
где <->
соответствует любому числу. Это квалификатор glob (.)
, который обеспечивает соответствие только обычным файлам. Измените весь шаблон на , *.*(.)
если вы хотите сопоставить имя файла любого обычного файла, которое содержит точку; мы требуем, чтобы точка присутствовала, так как вы хотите удалить расширение имени файла позже. Чтобы использовать более строгий, а не более свободный шаблон, используйте что-то вроде filename-<->.ext<->(.)
. Этот шаблон требует, чтобы вы знали, что вы хотите сопоставить имена, начинающиеся со строки filename-
.
Результатом расширения $name:r
будет то же значение, что и $name
, но без расширения (т.е. только «корень» значения).
Мы используем его --
, чтобы обозначить конец списка вариантов для обоих mkdir
, а также mv
чтобы имена, начинающиеся с тире, не принимались за варианты.
решение2
С zmv
:
autoload -Uz zmv # best in ~/.zshrc
mkmv() { mkdir -p -- $2:h && mv -- "$@"; }
zmv -P mkmv -n '(*).ext<->(#q.)' '$1/$f'
(удалить -n
, когда будете довольны).
Таким образом, вы воспользуетесь преимуществами zmv
проверок работоспособности.
решение3
Используйте этот for
цикл, который совместим с оболочками bash и zsh:
for file in *.*; do
[ -f "$file" ] &&
mkdir -p -- "${file%.*}" &&
mv -- "$file" "${file%.*}/"
done
-p
опцияmkdir
гарантирует, что файл существует, ошибка не возникает%.*
удаляет расширение из имен файлов.
решение4
С помощью GNU parallel (инструмента, мощь которого я только начинаю осознавать):
ls | parallel 'mkdir -p {.}; mv -i {} {.}'
Поскольку это никак не связано с процессором, я полагаю, что это parallel
не даст никаких преимуществ в скорости, но это все равно короче, чем многие другие приведенные решения.
И это не зависит от шаблона имен файлов.