在一个大驱动器中,我有数百个文件,每个文件的名称不同,但带有一些正则表达式。每个附加随机数字,有时附加字符。
例如,有 30 个文件以单词“television”的变体命名,另外 50 个文件以“lightbulb”的变体命名。
问题是,由于多年前我在实现生成名称的脚本时采用了糟糕的编码实践,所以它非常不一致;所以我们可能会得到:
- 电视139443.png
- 电视244904.png
- televisio097798.png
- elevisio984882.png
- _televisi90890890.png
- TV-911181.png
你明白了。该模式适用于所有文件“类别”——“电视”、“灯泡”、“汽车”等。幸运的是,每个文件名中至少有 5 个一致的字符,这些字符在其他类别中不会重复(十几只猫) )。
我想要实现的是递归地遍历混合文件夹,使用正确的完整标题+附加创建日期重命名每个文件,并将它们移到各自的文件夹中。
有点像:
case : regex("levis"):
rename to Television-($creation_date).($extension)
mv to ~/Categories/Television/
break;
case : regex("ghtbu"):
rename to Lightbulb-($creation_date).($extension)
mv to ~/Categories/Lightbulbs/
break;
显然这不是正确的代码;这只是为了说明这个想法。我对日常 bash/zsh 任务相当满意,但不够流畅!
我还需要优雅地处理没有正则表达式匹配的情况。
答案1
使用zsh
,您可以根据其近似匹配功能使用不同的方法:
autoload zmv # best in ~/.zshrc
zmodload zsh/stat
dest=~/Categories
categories=(television lightbulb motorcar etc)
mkdir -p -- $dest/${(C)^categories}
zmv -n '(**/)(*[^0-9])<->(.*)' \
'$dest/${c::=${${(CM)categories:#(#a3)$2}:-unknown}}/$c-$(
zstat -F %FT%T%z +mtime -- $f)$3'
${(M)categories:#(#a3)$2}
数组中的查找($2
数字之前的部分)categories
最多允许三个错误(不同字符、转置、插入、删除)。
如果正常,请移除-n
(空运行)。
对于你的方法,它可能是这样的:
autoload zmv # best in ~/.zshrc
zmodload zsh/stat
dest=~/Categories
typeset -A categories
categories=(
levis Television
ghtbu Lightbulb
otorc Motocar
)
mkdir -p -- $dest/$^categories
for k (${(k)categories}) (
zmv -n "(**/)*$k*(.*)" '$dest/$categories[$k]/$categories[$k]-$(
zstat -F %FT%T%z +mtime -- $f)$2'
)
或者如果常见的字符始终是第 3 个到第 7个:
autoload zmv # best in ~/.zshrc
zmodload zsh/stat
dest=~/Categories
categories=(Television Lightbulb Motocar)
mkdir -p -- $dest/$^categories
for c ($categories) (
zmv -n "(**/)*$c[3,7]*(.*)" '$dest/$c/$c-$(
zstat -F %FT%T%z +mtime -- $f)$2'
)
答案2
可能不是最有效的方法,但我会彻底解决问题,并将文件名中的字符串与目录相匹配,并且由于当前枚举是随机的,因此只需将它们计入即可保持唯一性。
homedir=~/Categories
eval homedir=$homedir
cats="Television Lightbulb Bananas Motorcar"
declare -A count
for cat in $cats; do if [ ! -d $homedir/$cat ]; then mkdir -p $homedir/$cat; fi; done
while read f; do
r=${f##*/}
r=${r%.*}
r=$(grep -Po "[a-z]+" <<<$r)
for cat in $cats; do
if [ $(grep -i "$r" <<< $cat) ]; then
((count[$cat]++))
mv "$f" $homedir/$cat/$cat-$(printf "%03g" ${count[$cat]}).${f##*.}
fi
done
done <<< $(find ./banana/ -type f)
不匹配的内容将grep
默默地退出循环,并留在原处以供稍后特别注意。
您想在文件名组合中包含的任何其他内容都可以输入,最后修改日期是
$(stat -c %Y $f)
ETC