6  循环实战:从“手动重复”到“批量自动化”的效率革命

6.1 【导语】万事之源:为何要这样做?

高通量测序的本质决定了其数据产出的“复数”特性。一个典型的项目往往涉及数十个乃至上百个样本。面对如此体量的数据,手动对每一个文件重复执行相同的分析命令,不仅是一种无法接受的低效率,其更致命的缺陷在于,它无法保证处理流程的绝对一致性。每一次手动的复制粘贴,都为引入人为操作的系统性偏差打开了方便之门。

因此,for循环的引入,其核心科学目标并非仅仅是“图省事”,而是“用代码来约束一致性”。它将一个经过验证的、标准化的分析流程,以一种精确、无差别的方式,施加于所有待处理的样本之上。这彻底根除了操作者依赖的变异,是保证最终分析结果具有科学可比性的、不可或缺的计算基础。

6.2 【核心实践】从原理到决策

6.2.1for 循环的语法解构】

for循环的语法结构 for sample in list; do ...; done 是自动化处理的基石。为了直观理解,我们可以将其比喻为一台全自动的移液工作站。

在这个比喻中,list (例如 *.fastq.gz) 就是你放在工作站台面上、等待处理的96孔板。sample 这个变量,则代表了机械臂当前正在精准操作的那一个特定的孔。而dodone之间包裹的所有命令,就是你为这台机器设定的一套标准操作程序(SOP),它将被不折不扣地执行在每一个孔上。

6.2.2 【决策一:如何定义你的list

如何精确地定义待处理的“96孔板”(即文件列表),是循环成功的第一个关键决策。

使用通配符*是最常见的方式。for file in *_R1.fastq.gz 是一项非常精确的决策,其意图是明确告知系统:“只处理当前目录下,所有以_R1.fastq.gz结尾的文件”。这确保了我们只针对Read 1文件执行操作,避免了混淆。

for file in *_R1.fastq.gz
do
    echo "Found file: $file"
done

使用命令输出则是另一种更灵活的决策。for srr in $(cat srr_list.txt) 的决策逻辑是:“处理对象并非由文件名模式决定,而是由srr_list.txt这个文件中明确列出的清单决定”。这在需要从NCBI批量下载数据时极为有用。

## srr_list.txt contains SRR numbers
## one per line, e.g., SRR000001

for srr in $(cat srr_list.txt)
do
    echo "Will process SRR: $srr"
done

6.2.3 【决策二:如何在循环中引用变量】

变量$sample是整个循环的灵魂,它是在do...done代码块与list之间传递信息的桥梁,代表了当前正在被处理的那个样本。

然而,仅仅引用$sample是不够的。真正的自动化,关键在于能够基于输入变量,动态地生成对应的输出名称。output=${sample%_R1.fastq.gz} 就是这样一项高级决策。这里的 % 是一种模式匹配操作,其含义是“从变量$sample的末尾,删除_R1.fastq.gz这部分”。这项决策的本质,是从输入文件名中,自动、精准地提取出样本的核心ID,这是实现全自动、无冲突输出的关键。

6.2.4 【实战:批量运行FastQC】

现在,我们将这些决策组合起来,构建一个完整的批量质控脚本。

## 创建一个用于存放结果的目录
OUT_DIR="01_fastqc_results"
mkdir -p $OUT_DIR

## 遍历所有的 Read 1 文件
for r1_file in *_R1.fastq.gz
do
    echo "正在对 $r1_file 运行 FastQC"
    
    # 执行命令
    fastqc $r1_file -o $OUT_DIR
done

echo "所有 FastQC 任务已完成。"

在这个实战案例中,我们首先决策创建一个独立的输出目录$OUT_DIR。然后在循环中,fastqc命令的-o参数直接使用了这个预定义的目录变量。这是一个清晰、可靠的输出管理策略。

6.3 【认知升维】常见的思维陷阱与对策

6.3.1 思维陷阱一:文件名中包含空格

空格是for循环的头号杀手。因为Shell默认使用空格作为分隔符来构建list。如果你的文件名是Sample 1_R1.fastq.gzfor循环会错误地将其切分为Sample1_R1.fastq.gz等多个部分,导致循环体内的命令接收到错误的、不完整的文件名,最终执行异常。

Johnson给出的对策是,建立一条不可逾越的铁律:在生物信息学分析的任何环节,所有文件和目录的命名,永远、只能使用字母、数字、下划线_或短横线-的组合。绝不使用空格、括号、问号等任何特殊字符。切记!

6.3.2 思维陷阱二:循环内输出相互覆盖

这是一个新手极易犯的逻辑错误。如果在循环中,生成结果文件的命令被硬编码为固定的名称,例如fastqc $file -o ./fastqc_results/result.html,那么每一次循环都会生成一个名为result.html的文件,并无情地覆盖掉上一次循环的成果。当循环结束时,你将只得到最后一个样本的结果。

这里强制要求:循环体内任何生成输出的命令,其输出文件名或路径中,必须包含来自输入变量$sample的可变部分。例如,fastqc $file -o ./${file}_fastqc/,通过为每个输入文件创建一个以其自身名字命名的子目录来存放结果,从而从根本上杜绝了输出覆盖的风险。

6.4 【总结与拓展】构建你的思维框架

我们必须将for循环内化为解决“一对多”问题的通用计算范式。在面对任何需要批量处理的任务时,都应首先将其分解为两个核心的、可定义的问题:

第一,“多”是什么?即,我需要处理的对象集合是什么?我应如何使用通配符或命令来精确地定义这个list

第二,“一”是什么?即,对于这个集合中的任意一个体,我需要执行的标准操作流程(SOP)是什么?这个流程应该如何在do...done代码块中被清晰地定义?

基于此框架,请思考一个更具挑战性的启发性问题:你现在面对一批双端测序数据,文件名格式高度一致,如 SampleA_R1.fastq.gzSampleA_R2.fastq.gzSampleB_R1.fastq.gzSampleB_R2.fastq.gz等等。你将如何设计一个for循环,使其能够一次性识别出样本的核心前缀(如 SampleA),并在单次循环体内,同时获得并处理该样本对应的R1和R2两个文件?


探索生命科学前沿,提升实战技能!欢迎微信搜索并加入「生信实战圈」,获取最新技术干货、实战案例与行业动态。 点击关注,与同行一起成长!