递归地为所有图片添加前缀

5

我有一个包含5000张JPG扩展名图片的文件夹。

我想要做的是,递归地向所有图像添加"thumb_"前缀。

我找到了一个类似的问题:重命名文件和目录(添加前缀),但我只想将前缀添加到扩展名为JPG的文件中。


1
可能是重复问题:重命名文件和目录(添加前缀),只需将'*'替换为'*.jpg'即可。 - Johan
不,这不是重复的。 - Abhishek Anand
7个回答

5

可能的解决方案之一:

find . -name '*.jpg' -printf "'%p' '%h/thumb_%f'\n" | xargs -n2  echo mv

原则:查找所有需要的文件,并为标准的 mv 命令准备参数。
注:
  • mv 的参数用'括起来,以允许文件名中包含空格。
  • 缺点是:如果文件名本身包含撇号(如许多 mp3 文件),则无法使用此方法。如果您需要移动更奇怪的文件名,请参见下文。
  • 上述命令只是模拟运行(仅显示带有参数的 mv 命令)。要实际操作,请删除 echo
任何文件名重命名都需要在 shell 中使用分隔符。问题在于,文件名(存储在 shell 变量中)通常可能包含分隔符本身,因此:
mv $file $newfile         #will fail, if the filename contains space, TAB or newline
mv "$file" "$newfile"     #will fail, if the any of the filenames contains "

正确的解决方案有两种:

  • 准备一个带有适当转义的文件名
  • 使用一种脚本语言,能够轻松理解任何文件名

bash中准备正确的转义是可能的,使用其内部的printf%q格式化指令 = 打印引用。但这种解决方案又长又无聊。

在我看来,最简单的方法是使用perl和零填充的print0,如下所示。

find . -name \*.jpg -print0 | perl -MFile::Basename -0nle 'rename $_, dirname($_)."/thumb_".basename($_)'

使用Perl的强大功能来修改文件名,最终将文件重命名。

3
请注意文件名中的空格(在 for ... in ... 语句中会出现错误),并且要知道 find . ... 的结果将始终以 ./ 开头(因此会尝试给出像 thumb_./file.JPG 这样不太正确的名称)。
因此,在所有情况下都正确地完成这个任务并不是一件微不足道的事情。我找到的正确工作表达式(包括空格、子目录等)是:
find . -iname \*.JPG -exec bash -c 'mv "$1" "`echo $1 | sed \"s/\(.*\)\//\1\/thumb/\"`"' -- '{}' \;

即使是这样也可能违反某些名称(带有引号)...

3
在OS X 10.8.5系统中,find命令没有-printf选项。似乎包含rename的端口依赖于一个安装时间长达数小时的WebkitGTK开发包。
对于我来说,这个单行的递归文件重命名脚本起到了作用:
find . -iname "*.jpg"  -print | while read name; do cur_dir=$(dirname "$name"); cur_file=$(basename "$name"); mv "$name" "$cur_dir/thumb_$cur_file"; done

我实际上是给CakePHP视图文件添加了一个“admin_”前缀,以将它们全部移动到管理部分。


您可以随时安装findutils macport - 它包含带有-printf的gnu-find命令(命令:gfind)。 - clt60
只是想指出,每一行都出现了文件未找到的错误,但它按照承诺工作了。 - Mars

2

您可以使用相同的答案,只需使用*.jpg而不是*


1

for file in *.JPG; do mv $file thumb_$file; done

如果它在当前目录下的多个目录级别中:

for file in $(find . -name '*.JPG'); do mv $file $(dirname $file)/thumb_$(basename $file); done


证明:

jcomeau@intrepid:/tmp$ mkdir test test/a test/a/b test/a/b/c
jcomeau@intrepid:/tmp$ touch test/a/A.JPG test/a/b/B.JPG test/a/b/c/C.JPG
jcomeau@intrepid:/tmp$ cd test
jcomeau@intrepid:/tmp/test$ for file in $(find . -name '*.JPG'); do mv $file $(dirname $file)/thumb_$(basename $file); done
jcomeau@intrepid:/tmp/test$ find .
.
./a
./a/b
./a/b/thumb_B.JPG
./a/b/c
./a/b/c/thumb_C.JPG
./a/thumb_A.JPG
jcomeau@intrepid:/tmp/test$

正如参考问题的答案所说,您需要考虑带空格的文件名。 - Ashish Patil
1
@MadisonFord 如果你的文件扩展名是 .jpg 而不是 .JPG,这在普通的 Linux 环境中将无法工作。Bash 是大小写敏感的,通常情况下。shopt -s nocaseglob 将关闭大小写敏感性。 - Spencer Rathbun
@MadisonFord,文件名中的空格是否会造成问题?您粘贴了哪个命令? - jcomeau_ictx

1

使用rename来实现:

rename 's/(\w{1})\.JPG$/thumb_$1\.JPG/' `find . -type f -name *.JPG`

-1

只针对当前文件夹中的jpg文件

for f in `ls *.jpg` ; do mv "$f" "PRE_$f" ; done

我需要在所有文件夹中执行此操作(递归),我有超过300个带有5000多张图像的文件夹。 - MadisonFord
1
那是错误的。几乎永远不要解析 ls 的输出。尝试一个文件 touch "a b c.jpg"。相反,使用 for f in *.jpg 会好得多。 - user unknown
使用@jcomeau_ictx的答案,只需在移动文件时加上"" - Ashish Patil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接