假设我的目录中有一堆文件
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 shell 兼容的循环:
for file in *.*; do
[ -f "$file" ] &&
mkdir -p -- "${file%.*}" &&
mv -- "$file" "${file%.*}/"
done
-p
确保文件存在的选项mkdir
不会引发错误%.*
从文件名中删除扩展名。
答案4
使用 GNU 并行(我刚刚开始意识到其强大功能的工具):
ls | parallel 'mkdir -p {.}; mv -i {} {.}'
由于这无论如何都不受 CPU 限制,我想parallel
不会带来任何速度优势,但它仍然比许多其他给定的解决方案短。
这不依赖于文件名的模式。