3. 第一个 Snakefile:规则的基本结构与执行
好,咱们终于要动手写真正的 Snakefile 了。
前面两章我们搭好了环境,也理解了 Snakemake 到底想解决什么问题。这一章,我们就从零开始,写一个最简单的规则,然后把它跑起来。
你想想看,Snakemake 的核心其实就是一堆“规则”。每个规则告诉它:“我有这些输入,我要产生这些输出,我用这个命令来办到。”就这么简单。
3.1 规则的最小骨架
一个规则长得什么样子?我直接给你看个例子。这是我做第一个质控流程时写的原型:
rule fastqc_raw:
input:
"data/sample1.fastq"
output:
"results/fastqc_raw/sample1_fastqc.html"
shell:
"fastqc data/sample1.fastq -o results/fastqc_raw/"
看懂了吗?说白了,就是在告诉 Snakemake 三件事:
- input:你需要什么文件才能开始干活。
- output:你承诺会造出什么文件。
- shell:你打算用什么命令来实现。
嗯,这里要注意。Snakemake 的规则本身不关心你 shell 里写了多复杂的命令。它只关注两样东西:文件的依赖关系和产出关系。
我个人习惯是,哪怕规则再简单,也一定把 input 和 output 写清楚。为什么?因为后面一旦规则多了起来,Snakemake 需要靠这些信息来画“依赖图”,决定先跑谁、后跑谁。
3.2 输入与输出的定义细节
输入和输出看着简单,但我建议你一开始就养成良好的组织习惯。
| 要素 | 属性 | 类型 | 说明 |
|---|---|---|---|
| input | 必需 | 路径字符串 / 列表 / 字典 | 声明规则运行前必须存在的文件 |
| output | 必需 | 路径字符串 / 列表 / 字典 | 声明规则运行后会生成的文件 |
| shell | 可选(与 run 二选一) | 字符串 | 直接在 shell 中执行的命令 |
| run | 可选(与 shell 二选一) | Python代码块 | 用 Python 写执行逻辑 |
我在项目中遇到过一种情况:有人把 output 写成了目录而不是具体文件名。结果 Snakemake 老是报错说“output 文件未找到”。因为 Snakemake 期望 output 是一个具体的文件路径,而不是一个文件夹。
3.3 执行你的第一个 Snakefile
好,文件写好了,怎么跑?很简单,在终端里切换到 Snakefile 所在的目录,然后敲:
snakemake -j 1
这条命令的意思是:“Snakemake,帮我跑起来,最多用 1 个 CPU 核。” 它会自动去找当前目录下名叫 Snakefile 的文件,然后解析里面的规则。
执行的时候,你会看到类似这样的输出:
Building DAG of jobs...
Using shell: /bin/bash
Provided cores: 1
Rules claiming more threads will be scaled down.
Job stats:
job count
fastqc_raw 1
total 1
Select jobs to execute...
[Sun Mar 24 10:15:32 2025]
rule fastqc_raw:
input: data/sample1.fastq
output: results/fastqc_raw/sample1_fastqc.html
jobid: 0
reason: Missing output files: results/fastqc_raw/sample1_fastqc.html
resources: tmpdir=/tmp
Running fastqc data/sample1.fastq -o results/fastqc_raw/
Analysis complete for sample1.fastq
[Sun Mar 24 10:15:45 2025]
Finished job 0.
1 of 1 steps (100%) done
看到了吗?Snakemake 很贴心,它会把“为什么跑这个规则”写在 reason 那一行里。上面显示的是:“因为 output 文件不存在,所以要跑。”
我第一次看到这个日志的时候,心里踏实多了。它把整个决策过程都摊开给你看。
3.4 常用执行参数一览
除了 -j,你还会经常用到下面这几个参数。我把它列成表格,方便你查阅。
| 参数 | 示例 | 作用 |
|---|---|---|
-j / --cores |
snakemake -j 4 |
指定使用的 CPU 核心数 |
-n |
snakemake -n |
干跑模式,只显示计划,不实际执行 |
-f |
snakemake -f |
强制重新运行指定的规则 |
-R |
snakemake -R fastqc_raw |
重新运行某个规则及其下游规则 |
--dag |
snakemake --dag | dot -Tpng > dag.png |
打印依赖图(需要 graphviz) |
snakemake -n 跑一遍干跑模式。它能检查出很多低级错误,比如文件路径写错、缩进不对等。干跑模式下 Snakemake 不会真的执行命令,很安全。
3.5 规则执行的核心逻辑
这一张 SVG 图,是我自己为团队培训时画的。它展示了 Snakemake 拿到一个 Snakefile 后,脑子里的处理流程。你看完就会明白,为什么它这么擅长管理工作流。
这张图总结了 Snakemake 的“大脑”是如何工作的。每次执行,它都先解析整个 Snakefile,构建一个有向无环图(DAG),然后逐个检查 output 文件是否已经存在。缺失了,它就去跑;已经在了,它就跳过。
我曾经遇到过一个小白同事,他把 input 和 output 写反了,结果 Snakemake 一顿猛跑,越跑越多,永远跑不完。你想想看,output 不存在所以跑规则,但跑完又没有生成 output……那不就是死循环了吗?
嗯,记住一条黄金法则:output 是规则的承诺,input 是规则的需求。千万别搞反。
3.6 避坑指南:常见执行错误
写第一个 Snakefile 的时候,有几个地方特别容易栽跟头。我帮你列出来,你遇到了可以直接对照。
- 缩进错误:Snakemake 使用 Python 风格的缩进。input、output、shell 必须对齐。我刚开始写的时候,总是不小心混用 Tab 和空格,结果解析报错。
- 路径问题:output 目录必须事先存在,或者你的命令里要能自动创建。否则 Snakemake 会说“无法写入文件”。我记得有一次跑批量分析,忘记建目录,前100个样本白跑了。
- 忘记写版本:如果你同时有多个 Snakefile 版本在目录下,Snakemake 默认只认名字叫
Snakefile的那个。可以用-s参数指定其他文件:snakemake -s my_other_snakefile。
核心原则回顾:
- 每个规则必须有明确的 input 和 output。
- output 必须是具体的文件名,不是目录。
- 执行前先用
snakemake -n做干跑检查。 - Snakemake 靠 output 是否存在来判断是否需要执行。
好了,现在你已经掌握了规则的基本结构和执行方法。试着在你的实验目录里建一个 Snakefile,放上一个真实的 fastq 文件,跑一下 snakemake -j 1。亲眼看到 Snakemake 帮你把质控跑完,那种感觉还是很爽的。