2.12 批量重命名和移动

重命名多个文件是我们经常会碰到的一项工作。举个简单的例子,当你把照片从数码相机传输到你的计算机之后,你可能会删除其中某些不如意的部分,这会使图像文件的编号变得不再连续。于是你会想使用特定的前缀和连续的数字对它们进行重命名。我们当然可以借助第三方软件执行这类重命名操作,但是我们也可以使用Bash命令在短短几秒钟之内完成同样的工作。

另一件经常要做的工作是,将文件名中包含某个特定部分(例如相同的前缀)或者具有特定类型的所有文件移动到指定的文件夹中。让我们看看如何用脚本来执行这些操作。

2.12.1 预备知识

rename命令利用Perl正则表达式修改文件名。综合运用findrenamemv,我们其实可以完成很多操作。

2.12.2 实战演练

用特定的格式重命名当前目录下的图像文件,最简单的方法是使用下面的脚本:

  1. #!/bin/bash
  2. #文件名 rename.sh
  3. #用途: 重命名 .jpg 和 .png 文件
  4. count=1;
  5. for img in *.jpg *.png
  6. do
  7. new=image-$count.${img##*.}
  8. mv "$img" "$new" 2> /dev/null
  9. if [ $? -eq 0 ];
  10. then
  11. echo "Renaming $img to $new"
  12. let count++
  13. fi
  14. done

输出如下:

  1. $ ./rename.sh
  2. Renaming hack.jpg to image-1.jpg
  3. Renaming new.jpg to image-2.jpg
  4. 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会自动扩展为:

  1. 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

  1. $ rename *.JPG *.jpg

将文件名中的空格替换成字符“_”:

  1. $ rename 's/ /_/g' *

# 's/ /_/g'用于替换文件名,而 * 是用于匹配目标文件的通配符,它也可以以 *.txt或其他样式出现。

另外,你也可以转换文件名的大小写:

  1. $ rename 'y/A-Z/a-z/' *
  2. $ rename 'y/a-z/A-Z/' *

为了将所有的 .mp3文件移入给定的目录,可以使用:

  1. $ find path -type f -name "*.mp3" -exec mv {} target_dir \;

将所有文件名中的空格替换为字符“_”:

  1. $ find path -type f -exec rename 's/ /_/g' {} \;