2. Snakemake最小可运行流程
上一节我们理解了 Snakemake 用“规则”描述“如何从输入生成输出”。这一节不再停留在概念层面,而是用一条真实的质控命令,构建出第一个可以真正跑起来的流程。目标很明确:让 Snakemake 知道要生成什么文件、用什么命令生成、以及如何在不实际执行的情况下先检查计划是否正确。
先看一个最小但完整的 Snakefile。假设我们有一份双端测序数据 data/sample_R1.fastq.gz 和 data/sample_R2.fastq.gz,希望用 fastp 做质控,输出到 results/ 目录。可以写成:
rule all:
input:
"results/sample.clean_R1.fastq.gz",
"results/sample.clean_R2.fastq.gz"
rule fastp_qc:
input:
r1="data/sample_R1.fastq.gz",
r2="data/sample_R2.fastq.gz"
output:
o1="results/sample.clean_R1.fastq.gz",
o2="results/sample.clean_R2.fastq.gz",
html="results/sample.fastp.html",
json="results/sample.fastp.json"
threads: 4
shell:
"fastp -i {input.r1} -I {input.r2} "
"-o {output.o1} -O {output.o2} "
"-h {output.html} -j {output.json} "
"-w {threads}"
这段代码虽然短,却包含了 Snakemake 最核心的五个要素:规则、输入、输出、目标文件和执行命令。下面逐一拆开。
规则:把一条命令包装成可调度的单元
rule fastp_qc: 定义了一个名为 fastp_qc 的规则。规则名只是标识符,真正决定行为的是它内部的 input、output 和 shell。Snakemake 的调度逻辑是“反向推导”:从目标文件出发,寻找哪个规则的 output 能匹配它,再检查该规则的 input 是否存在;如果不存在,继续寻找能生成该 input 的规则,直到所有输入都落到磁盘上的真实文件或由其他规则生成。这种“从结果倒推过程”的方式,正是 Snakemake 区别于普通脚本的关键。
输入与输出:声明依赖,而不是写死路径
input 和 output 中可以使用命名参数,例如 r1=、o1=。命名之后,在 shell 里就能用 {input.r1}、{output.o1} 引用,避免重复书写长路径,也降低改错文件的风险。注意:output 里列出的文件,Snakemake 会认为“规则执行成功后这些文件应当存在”。如果命令跑完但某个输出文件没有生成,Snakemake 会报错,提示输出缺失。因此,output 必须与命令实际产生的文件严格一致。
另外,threads: 4 声明该规则使用 4 个线程,{threads} 会替换为这个数字。fastp 的 -w 参数正是线程数,这样写可以让线程配置集中管理,而不是散落在命令字符串里。
通配符:让一条规则处理多个样本
上面的写法只能处理一个样本。如果样本变成 sampleA、sampleB、sampleC,逐个写规则显然不现实。通配符(wildcard)就是为解决这个问题而存在的。把规则改写成:
rule fastp_qc:
input:
r1="data/{sample}_R1.fastq.gz",
r2="data/{sample}_R2.fastq.gz"
output:
o1="results/{sample}.clean_R1.fastq.gz",
o2="results/{sample}.clean_R2.fastq.gz",
html="results/{sample}.fastp.html",
json="results/{sample}.fastp.json"
threads: 4
shell:
"fastp -i {input.r1} -I {input.r2} "
"-o {output.o1} -O {output.o2} "
"-h {output.html} -j {output.json} "
"-w {threads}"
这里的 {sample} 就是通配符。当 Snakemake 需要生成 results/sampleA.clean_R1.fastq.gz 时,它会用 sampleA 去匹配 {sample},从而推导出输入应为 data/sampleA_R1.fastq.gz。通配符的值不是提前写死的,而是由目标文件名反向填充的。理解这一点,就理解了 Snakemake 最强大的自动化能力。
目标文件:告诉 Snakemake“我要什么”
Snakemake 默认把 Snakefile 中第一条规则的 output 当作目标。因此通常写一个 rule all 放在最前面,专门收集最终需要的文件:
rule all:
input:
"results/sampleA.clean_R1.fastq.gz",
"results/sampleA.clean_R2.fastq.gz",
"results/sampleB.clean_R1.fastq.gz",
"results/sampleB.clean_R2.fastq.gz"
rule all 本身不执行任何命令,它只声明“最终产物清单”。Snakemake 会为清单中的每个文件寻找生成规则,并自动展开成完整的任务图。也可以在命令行显式指定目标:snakemake results/sampleA.clean_R1.fastq.gz,此时只有生成该文件所需的任务会被执行。
dry-run:先看计划,再决定是否执行
写流程最怕的是“命令写错,一跑就覆盖或删错文件”。Snakemake 提供了 -n(即 --dry-run)选项,只打印将要执行的规则和命令,不真正运行:
snakemake -n -p
-n 表示 dry-run,-p 表示打印 shell 命令。典型输出会列出每个待执行任务的编号、规则名、输入输出文件以及展开后的命令。如果输出中出现了你预期之外的文件路径,或者某个样本没有被纳入,就说明通配符匹配或目标文件声明有问题,此时应修改 Snakefile,而不是直接执行。dry-run 是 Snakemake 工作流开发中最高频的检查手段,建议每次修改规则后都先跑一遍。
从目标文件到执行计划的完整链路
把上面的要素串起来,可以用一张图看清 Snakemake 的解析过程:目标文件如何触发规则匹配,通配符如何被填充,输入如何被检查,最终形成待执行任务。
图中展示的是一条“目标文件 → 规则匹配 → 通配符填充 → 输入检查 → 命令展开 → 执行计划”的解析链路。实际使用时,你只需要维护 rule all 中的目标文件列表和带通配符的规则,Snakemake 会自动完成中间所有推导。dry-run 则是在这条链路末端插入一个“只读检查点”,让你在真正消耗计算资源之前确认计划无误。
一个可直接使用的检查清单
在把流程交给集群或长时间运行之前,建议按下面几项快速核对:
- 目标文件是否完整:
rule all的 input 是否覆盖了所有样本、所有需要保留的产物(如 clean fastq、html、json)。 - 通配符是否一致:input 和 output 中的
{sample}拼写是否相同,路径前缀是否与真实数据目录匹配。 - output 是否与命令一致:shell 中实际写出的文件,是否都在 output 中声明;output 中声明的文件,命令是否真的会生成。
- dry-run 输出是否合理:任务数量是否等于预期样本数,命令中的路径是否指向正确位置,线程数是否符合资源规划。
- 是否误用绝对路径:尽量使用相对路径,便于整个项目目录迁移后仍可运行。
data/ 目录下的文件名是否与通配符模式一致,再检查 rule all 是否遗漏。