第一个 Snakefile:规则定义、输入输出、运行 snakemake

好,我们正式开始动手了。你可能会想,Snakefile 到底长什么样?说白了,它就是一个纯文本文件,里面写着一系列“规则”。每条规则告诉 Snakemake:你想要什么文件,以及怎么做出来。

我第一次写 Snakefile 时,最不习惯的就是“声明式”思维——不是写“先做A,再做B”,而是写“我要 B,而 B 依赖于 A”。这种反转一开始有点绕,但习惯之后你会发现,这才是管理复杂依赖的正确姿势。

规则的基本骨架

每个规则,核心就三部分:名字、输入、输出,再加上具体的执行命令。看下面这个最简例子:

rule all:
    input:
        "result.txt"

这是一个特殊的规则,名为 all。它没有输出,也没有执行块 —— 它只是告诉 Snakemake:“嘿,我最终要生成这个文件”。Snakemake 会沿着依赖链反向推导,找到所有需要执行的步骤。

再看一个真正的处理规则:

rule count_lines:
    input:
        "data/raw.txt"
    output:
        "result.txt"
    shell:
        "wc -l {input} > {output}"

这里 count_lines 规则读入一个原始文件,统计行数,把结果写入输出文件。{input} 和 {output} 是 Snakemake 提供的占位符,运行时会自动替换成实际路径。我个人习惯用花括号来引用输入输出,这样写 shell 命令特别清爽。

小提示: 规则名可以任意取,但建议用有意义的英文单词或下划线组合。Snakemake 不要求规则名唯一,但重复名会导致覆盖,实际项目中小心。

输入与输出

输入和输出可以是一个文件,也可以是多个文件。语法很简单:

文件数量 写法示例 占位符
单个 input: "a.txt" {input}
多个命名 input: reads="sample1.fq", ref="genome.fa" {input.reads}、{input.ref}
多个匿名 input: ["a.txt", "b.txt"] {input[0]}、{input[1]}

输出也类似:

rule merge:
    input:
        "1.txt",
        "2.txt"
    output:
        "merged.txt"
    shell:
        "cat {input} > {output}"

这里的 {input} 会被展开成空格分隔的多个文件路径。Snakemake 会自动处理列表展开,你只需要写一次命令。

注意: 输出文件路径必须与最终生成的文件路径完全一致。我曾经因为路径大小写写错,排查了半天才发现 Snakemake 认为文件未生成,导致每次重跑。建议用绝对路径或者统一用相对路径,不要混用。

运行你的第一个工作流

有了 Snakefile,运行只需要一行命令。假设 Snakemake 已经安装好,并且当前目录下有一个 data/raw.txt 文件:

conda activate snakemake_env
snakemake -c 1

-c 1 表示使用 1 个 CPU 核心。Snakemake 会读取当前目录下的 Snakefile(默认文件名),检查 result.txt 是否存在。如果不存在,它会执行 count_lines 规则。如果 data/raw.txt 也比 result.txt 新,Snakemake 也会重新执行。

你可能会看到类似这样的输出日志:

Building DAG of jobs...
Job counts:
	count	jobs
	1	count_lines
	1

[Wed Jan 15 10:00:00 2025]
rule count_lines:
    input: data/raw.txt
    output: result.txt
jobid: 0
Reason: Missing output files: result.txt
Executing...
1 lines written to result.txt
Finished job 0.
1 of 1 steps (100%) done

看到 100% done 就表示你的第一个工作流跑通了。是不是很简单?嗯,确实就这几步。

cat result.txt
1000

如果你用 snakemake -c 1 --dryrun,它不会真的执行,而是打印将要做什么。这个参数我在调试依赖关系时几乎天天用。

核心要点: Snakemake 只关心“输出文件”是否存在且是否最新。你不需要手动安排执行顺序,Snakemake 通过输入输出依赖自动构建一个有向无环图(DAG),然后按拓扑顺序执行。
Snakefile 规则执行流程 输入文件 input: "data/raw.txt" 规则 (rule) count_lines shell: wc -l ... 输出文件 output: "result.txt" Snakemake 自动执行 依赖检查 结果:result.txt 已生成 ✅

你看这张图,逻辑很直白:Snakemake 从最终输出出发,找到对应规则,检查输入是否存在,然后执行。如果输入文件缺失,它会向上查找生成输入文件的其他规则,形成完整的依赖链。

调试技巧: 如果你想看看 Snakemake 到底会执行哪些规则,加上 --dag 参数可以输出依赖图 DOT 格式,配合 dot -Tpng 生成可视化。不过现阶段,记住 --dryrun 就够用了。

好了,你的第一个 Snakefile 已经跑通了。从规则定义到输入输出,再到命令行运行,全流程就是这么回事。别被那些花里胡哨的特性吓到,基础其实只有这么几行代码。接下来你要做的就是——多写几个规则,让它们互相依赖,感受一下自动编排的威力。