通过组合find 和exec 实现
find [查找路径] [匹配条件] -exec [执行命令] \; 或 {} +
这是 find 命令的经典用法:先按条件查找文件,再对找到的文件执行指定操作。
例如
find /path -name "*.fq.gz" -exec sh -c 'for f;do mv $f ${f%.fq.gz}_R1.fq.gz;done' _ {} +
find /path:从 /path 目录开始(包括所有子目录)递归查找文件。
-name "*.fq.gz":只匹配文件名以 .fq.gz 结尾的文件(* 是通配符,表示任意字符)。
例如:会匹配 file1.fq.gz、subdir/file2.fq.gz 等。
这是核心执行部分,用于对找到的文件进行重命名,可拆分为以下子部分:
find 的参数,意思是 “对每个找到的文件执行后面的命令”。
启动一个新的 Shell 进程,并执行单引号中的命令。
这里的 sh 是 Shell 解释器(如果系统中 sh 是 bash 或 dash 等,会影响语法支持,后面会提到)。
这是实际处理文件的逻辑,是一个循环:
for f; do ... done:遍历所有传递给 Shell 的文件参数(f 是循环变量,代表当前处理的文件路径)。
mv $f ...:mv 是重命名 / 移动文件的命令,$f 是当前文件的原始路径(如 subdir/file.fq.gz)。
${f%.fq.gz}_R1.fq.gz:对文件名进行处理:
${f%.fq.gz}:使用 Shell 的参数扩展,从 $f 的末尾删除最短的 .fq.gz 后缀(例如 file.fq.gz 会变成 file)。
- 拼接
_R1.fq.gz 后,新文件名为 file_R1.fq.gz。
这是传递给 sh -c '...' 的第一个参数(即 $0),用于表示 Shell 脚本的 “名称”(占位符,无实际作用,通常用 _ 或 sh 代替)。
find 的占位符,表示 “将所有找到的文件路径一次性传递给前面的命令”(而 {} \; 是逐个传递)。
例如:如果找到 3 个文件,{} + 会把它们作为参数一次性传给 sh,效率更高。
命令执行步骤:
find 找到这两个文件,得到路径列表。
find 通过 {} + 将路径一次性传给 sh -c '...',相当于执行:
sh -c 'for f; do mv $f ${f%.fq.gz}_R1.fq.gz; done' _ /path/sub1/data1.fq.gz /path/sub2/data2.fq.gz
- 循环处理每个文件:
- 第一个文件:
mv /path/sub1/data1.fq.gz /path/sub1/data1_R1.fq.gz
- 第二个文件:
mv /path/sub2/data2.fq.gz /path/sub2/data2_R1.fq.gz
- 最终两个文件被重命名为带
_R1 后缀的版本。
Bad substitution 错误:如果系统的 sh 是 dash(不支持部分扩展语法),可能报错。此时可改用 bash -c 替代 sh -c。
- 文件名含特殊字符:如果文件名有空格、引号等,
$f 可能被解析错误,建议用双引号包裹:mv -- "$f" "${f%.fq.gz}_R1.fq.gz"(-- 避免文件名以 - 开头被误判为选项)。
数据路径复杂时更健壮的脚本如下
find /path -name "*.fq.gz" -exec bash -c 'for f; do mv -- "$f" "${f%.fq.gz}_R1.fq.gz"; done' _ {} +
或者也可以用//直接进行字符替换
find ./Results/ -name "*.fq.gz" -exec bash -c 'for f; do mv -- "$f" "${f/.fq.gz/_R1.fq.gz}"; done' _ {} +