2.12 批量重命名和移动
重命名多个文件是我们经常会碰到的一项工作。举个简单的例子,当你把照片从数码相机传输到你的计算机之后,你可能会删除其中某些不如意的部分,这会使图像文件的编号变得不再连续。于是你会想使用特定的前缀和连续的数字对它们进行重命名。我们当然可以借助第三方软件执行这类重命名操作,但是我们也可以使用Bash命令在短短几秒钟之内完成同样的工作。
另一件经常要做的工作是,将文件名中包含某个特定部分(例如相同的前缀)或者具有特定类型的所有文件移动到指定的文件夹中。让我们看看如何用脚本来执行这些操作。
2.12.1 预备知识
rename
命令利用Perl正则表达式修改文件名。综合运用find
、rename
和mv
,我们其实可以完成很多操作。
2.12.2 实战演练
用特定的格式重命名当前目录下的图像文件,最简单的方法是使用下面的脚本:
#!/bin/bash
#文件名 rename.sh
#用途: 重命名 .jpg 和 .png 文件
count=1;
for img in *.jpg *.png
do
new=image-$count.${img##*.}
mv "$img" "$new" 2> /dev/null
if [ $? -eq 0 ];
then
echo "Renaming $img to $new"
let count++
fi
done
输出如下:
- $ ./rename.sh
- Renaming hack.jpg to image-1.jpg
- Renaming new.jpg to image-2.jpg
- Renaming next.jpg to image-3.jpg
该脚本将当前目录下所有的.jpg和.png文件重命名,新文件名的格式为image-1.jpg、 image-2.jpg、image-3.jpg、image-4.png等,依次类推。
2.12.3 工作原理
在前文列出的重命名脚本中,我们使用for
循环对所有扩展名为.jpg的文件进行迭代,利用通配符*.jpg
和*.png
匹配所有的JPEG和PNG文件。我们可以稍稍改进一下扩展名匹配。.jpg
只能匹配小写的扩展名,而将.jpg
改成.[jJ][pP][gG]
就会使这项匹配不区分大小写,这时,它不仅可以匹配扩展名为.jpg的文件,也可以匹配扩展名为.JPG或.Jpg的文件。在Bash中,字符可以包含在[]
之中,这意味着只要某个字符属于[]
中的那个字符集合,[]
就能够匹配它。
上面代码中的for img in *.jpg *.png
会自动扩展为:
for img in hack.jpg new.jpg next.jpg
为了跟踪图像编号,我们初始化变量count=1
。下一步就是用mv
命令重命名文件。因此需要构造出新的文件名。${img##*.}
对处于当前循环中的文件名进行解析并获得文件扩展名(请参看2.11.2节中对于 ${img##*.}
的解释)。
let count++
用来在每次循环中增加文件编号。
你可以看到2>
操作符将mv
命令的标准错误(stderr
)重定向到 /dev/null。这是为了防止错误信息被打印到终端。
如果通配符 *.png
和 *.jpg
没有匹配到任何图像文件,那么shell会将通配符解释成一个字符串。从上面的输出中可以看到并不存在扩展名为.png的文件。因此,如果将 *.png
作为一个文件名并执行mv *.png image-X.png
,肯定会产生错误。if [ $? -eq 0 ]
语句用来检查退出状态($?
)。如果最近执行的命令没有错误,那么 $?
的值是0
,否则它会返回非0值。当mv
命令出错时,也会返回非0值,因此不会出现提示信息“Renaming file”,计数也不会增加。
还有许多其他的执行重命名操作的方法,这里再举几个简单的例子。
将 *.JPG
更名为 *.jpg
:
- $ rename *.JPG *.jpg
将文件名中的空格替换成字符“_
”:
- $ rename 's/ /_/g' *
# 's/ /_/g'
用于替换文件名,而 *
是用于匹配目标文件的通配符,它也可以以 *.txt
或其他样式出现。
另外,你也可以转换文件名的大小写:
- $ rename 'y/A-Z/a-z/' *
- $ rename 'y/a-z/A-Z/' *
为了将所有的 .mp3文件移入给定的目录,可以使用:
- $ find path -type f -name "*.mp3" -exec mv {} target_dir \;
将所有文件名中的空格替换为字符“_
”:
- $ find path -type f -exec rename 's/ /_/g' {} \;