linux下批量重命名目录及子目录下的文件

发布时间 2023-07-12 14:00:17作者: zenglihan

一 、加上后缀名

假如只是给当前目录及所有子目录下的文件添加后缀名,使用 findmv 就可以了。

比如把当前及子目录下所有带 _test 后缀的文件加上 .c 后缀
find . -type f -name '*_test' -exec mv {} {}.c \;

  • find . 查找当前及子目录,GNU版本的find也可以省略点号,效果一样。
  • -type f 指定普通文件类型 【-type d 则指定目录】
  • -name '*_test' 指定以 _test 后缀的文件名,* 匹配 0 个或多个字符,这里需要注意的是test后面加星号可以修改test在文件名中间的情况,不加星号则代表它就在文件名末尾
  • 单引号是强引用,保证星号在传递给find 时不会被shell解释,即将字面含义的星号传入find,find后将其解释为通配符。在这里可以使用反斜杠,即 \*_test = '*_test',这里涉及到引用和转义及强弱引用的知识。
    简单来说就是
  1. 反斜线引用单个字符(也称为转义了这个字符)
  2. 单引号引用一串字符
  3. 双引号引用一串字符,但是保留$(美元)、`(反引号)、(反斜线)的特殊含义
    ls *.c 显示所有后缀名为 .c 的文件,但 ls '*.c'显示字面意义上的 *.c文件,星号在这里被引用为字面上的 * 号, 失去其通配的含义
  • -exec 代表 find 的动作(action),语法为 -exec command {} \; 执行command, {} 指示匹配的文件名(路径名),对一些shell来说可能需要引用花括号-exec command '{}' \; 为了表明 -exec 命令的结尾,必须使用分号结束命令,同时为了避免被shell解释,又得加上反斜线 \
  • mv {} {}.c \; 这里 mv 即上面的 command ,{} 指示 find 查找到的路径名
    {}.c 为文件名加上后缀

下面来做个测试

点击查看代码
测试环境
$ Ubuntu 22.04.2 LTS (GNU/Linux 5.15.90.1-microsoft-standard-WSL2 x86_64)

$ touch xx{1,2,3,4,5,6}_test
$ ls
$ xx1_test  xx2_test  xx3_test  xx4_test  xx5_test  xx6_test
$ find -type f -name '*_test'
$ 
./xx4_test
./xx3_test
./xx6_test
./xx1_test
./xx2_test
./xx5_test
$ find . -type f -name '*_test' -exec mv {} {}.c \;
$ ls
$
xx1_test.c  xx2_test.c  xx3_test.c  xx4_test.c  xx5_test.c  xx6_test.c

没有什么问题

-exec的问题是每查找到一个文件,就会执行一次-exec命令,如果查到到六次(上面的情况),就会执行find命令六次,对于操作一小组的文件的简单命令可能没啥问题,但是涉及到大量文件时,效率就比较慢,这时可以将find的输出管道传送到另一个程序,即xargs,这个程序专门处理这种情况。这时,配合rename使用就可以处理更多情况

二、批量修改文件名的任意部分

我的rename程序为Perl版本,提供man rename可以查看版本
像上面那样只是加上后缀名,可以这样
find . -type f -name '*_test' | xargs rename 's/(test)/$1.c/'

  • | 为管道线,将find的输出到管道上再作为rename的输入
  • xargs使得rename程序可以对find查找到的路径名进行操作
  • 's/(test)/$1.c/' Perl版本的正则表达式,s/被替换的字符串/替换时用的字符串/,(test)将字符串包裹起来,然后用后面的$1指代该字符串(在其它正则表达式中,可能是\1而不是$1)

如果想要在_test前面加上log变成_logtest,简单的更改下正则表达式的内容即可
find . -type f -name '*_test' | xargs rename 's/(test)/log$1/'
假如test后面带有数字和后缀名,如_test1.c,_test2.c,_test3.c.....想要将其改成其他后缀名,只需
find . -type f -name '*_test*' | xargs rename 's/(test[0-9]).c)/$1.后缀名/'
这里面有许多花样,取决于对正则表达式的理解程度

进一步阅读:
regular-expressions
Harley Hahns Guide to Unix and Linux
The Linux Command Line