【发布时间】:2010-11-17 13:53:04
【问题描述】:
我有一个名为“images”的目录,其中包含大约一百万张图片。是的。
我想编写一个 shell 命令将所有这些图像重命名为以下格式:
原版: filename.jpg
新版: /f/i/l/filename.jpg
有什么建议吗?
谢谢,
丹
【问题讨论】:
标签: linux shell file directory rename
我有一个名为“images”的目录,其中包含大约一百万张图片。是的。
我想编写一个 shell 命令将所有这些图像重命名为以下格式:
原版: filename.jpg
新版: /f/i/l/filename.jpg
有什么建议吗?
谢谢,
丹
【问题讨论】:
标签: linux shell file directory rename
for i in *.*; do mkdir -p ${i:0:1}/${i:1:1}/${i:2:1}/; mv $i ${i:0:1}/${i:1:1}/${i:2:1}/; done;
${i:0:1}/${i:1:1}/${i:2:1} 部分可能是一个变量,或者更短或不同,但上面的命令可以完成工作。您可能会遇到性能问题,但如果您真的想使用它,请将 *.* 缩小到更少的选项(a*.*、b*.* 或适合您的选项)
编辑:在 i 之前为 mv 添加了一个 $,正如 Dan 所指出的那样
【讨论】:
${i:0:1} 语法是 bash-ism,这在 Linux 上可能没问题,但以防万一......
${i:0:7}/ 替换了${i:0:1}/${i:1:1}/${i:2:1}/,它就像一个魅力。
您可以使用例如 sed 生成新文件名:
$ echo "test.jpg" | sed -e 's/^\(\(.\)\(.\)\(.\).*\)$/\2\/\3\/\4\/\1/'
t/e/s/test.jpg
所以,你可以这样做(假设所有目录都已创建):
for f in *; do
mv -i "$f" "$(echo "$f" | sed -e 's/^\(\(.\)\(.\)\(.\).*\)$/\2\/\3\/\4\/\1/')"
done
或者,如果你不能使用 bash $( 语法:
for f in *; do
mv -i "$f" "`echo "$f" | sed -e 's/^\(\(.\)\(.\)\(.\).*\)$/\2\/\3\/\4\/\1/'`"
done
但是,考虑到文件的数量,您可能只想使用 perl,因为要产生很多 sed 和 mv 进程:
#!/usr/bin/perl -w
use strict;
# warning: untested
opendir DIR, "." or die "opendir: $!";
my @files = readdir(DIR); # can't change dir while reading: read in advance
closedir DIR;
foreach my $f (@files) {
(my $new_name = $f) =~ s!^((.)(.)(.).*)$!$2/$3/$4/$1/;
-e $new_name and die "$new_name already exists";
rename($f, $new_name);
}
perl 肯定仅限于同一文件系统,尽管您可以使用 File::Copy::move 来解决这个问题。
【讨论】:
-f $f or next; 在 perl foreach 循环的顶部,类似于 shell 循环)
您可以将其作为 bash 脚本执行:
#!/bin/bash
base=base
mkdir -p $base/shorts
for n in *
do
if [ ${#n} -lt 3 ]
then
mv $n $base/shorts
else
dir=$base/${n:0:1}/${n:1:1}/${n:2:1}
mkdir -p $dir
mv $n $dir
fi
done
不用说,您可能需要担心空格和短名称的文件。
【讨论】:
我建议一个简短的 python 脚本。大多数 shell 工具都会拒绝这么多输入(尽管 xargs 可能会起作用)。将在几秒钟内更新示例。
#!/usr/bin/python
import os, shutil
src_dir = '/src/dir'
dest_dir = '/dest/dir'
for fn in os.listdir(src_dir):
os.makedirs(dest_dir+'/'+fn[0]+'/'+fn[1]+'/'+fn[2]+'/')
shutil.copyfile(src_dir+'/'+fn, dest_dir+'/'+fn[0]+'/'+fn[1]+'/'+fn[2]+'/'+fn)
【讨论】:
任何在 shell 中使用通配符语法的建议解决方案都可能由于您拥有的文件数量过多而失败。在当前提出的解决方案中,perl 可能是最好的。
但是,您可以轻松地调整任何 shell 脚本方法来处理任意数量的文件,例如:
ls -1 | \
while read filename
do
# insert the loop body of your preference here, operating on "filename"
done
我仍然会使用 perl,但如果您仅限于使用简单的 unix 工具,那么将上述 shell 解决方案之一与我展示的循环结合起来应该可以帮助您。不过会很慢。
【讨论】:
seq 1 1000000 中的 i 有效。
for f in * 可以很好地处理 1,000,000 个文件。慢,但它有效。