3. Snakefile 语法入门归纳总结

Snakefile 是整个流程的大脑。
说白了,它就是一套用 Python 语法写的工作说明书。
你把每一步该做什么、用什么文件、产生什么结果写进去,Snakemake 就自动帮你按顺序执行,还能并行跑。

3.1 Snakefile 长得像什么

我第一次见到 Snakefile 时,还以为是 Makefile 的变种。
其实它就是个带扩展语法的 Python 文件。
每条核心指令叫 rule,一个规则定义一个处理步骤。

来看最简版本:

rule fastqc:
    input:
        "sample1.fastq"
    output:
        "sample1_fastqc.html",
        "sample1_fastqc.zip"
    shell:
        "fastqc sample1.fastq"

嗯,结构很清晰:输入、输出、要执行的命令。

3.2 规则的骨架:输入、输出、动作

每个 rule 至少需要这三样东西。我个人习惯先写输出,因为 Snakemake 靠输出来判断任务是否完成。

组件作用必须吗
input上游文件列表否(没有输入就是第一条规则)
output本规则产生的文件强烈建议
shell / run执行命令或 Python 代码是(至少选一个)

小技巧:输出文件最好带明确的样本名或批次号,方便后期排查。

3.3 动作的两种写法:shell vs run

shell 后面跟字符串命令,适合调用外部工具。
run 后面跟 Python 代码块,适合做逻辑处理或调用 Python 库。

rule count_lines:
    input:
        "sample.fastq"
    output:
        "counts.txt"
    run:
        import subprocess
        result = subprocess.run(["wc", "-l", input[0]], capture_output=True, text=True)
        with open(output[0], "w") as f:
            f.write(result.stdout)

我个人更推荐 shell——除非你需要复杂的条件判断,否则 shell 更简洁、更容易 debug。

3.4 通配符:让一条规则适配所有样本

如果每个样本写一条规则,你会疯掉的。
所以 Snakemake 用通配符 {样本名} 这种占位符,让一条规则处理所有文件。

rule fastqc_wildcard:
    input:
        "{sample}.fastq"
    output:
        "{sample}_fastqc.html",
        "{sample}_fastqc.zip"
    shell:
        "fastqc {input}"

当你让 Snakemake 生成 sampleA_fastqc.html 时,它会自动把 {sample} 替换成 sampleA。
我记得第一次用通配符时,一直纠结要不要写多字符模式,其实 {sample} 就能匹配任意文件名(不包含斜杠)。

3.5 规则依赖:Snakemake 如何决定顺序

Snakemake 不关心你写规则的先后顺序。
它只看 输出文件 是不是别的规则的 输入文件。
如果规则 B 的输入等于规则 A 的输出,Snakemake 就知道先跑 A 再跑 B。

例如:

rule trim:
    input:
        "{sample}.fastq"
    output:
        "{sample}_trimmed.fastq"
    shell:
        "cutadapt -o {output} {input}"

rule fastqc_trimmed:
    input:
        "{sample}_trimmed.fastq"
    output:
        "{sample}_trimmed_fastqc.html",
        "{sample}_trimmed_fastqc.zip"
    shell:
        "fastqc {input}"

当你请求 sampleA_trimmed_fastqc.html 时,Snakemake 自动推导出依赖链:先 trim 生成 trimmed.fastq,再 fastqc 生成 html。

3.6 用 all 规则定义最终目标

实际项目中,你不会一个个手动指定要哪些文件。
习惯上会在 Snakefile 开头写一条 rule all,把最终需要的文件列在 input 里。

rule all:
    input:
        expand("{sample}_trimmed_fastqc.html", sample=["A","B","C"])

这样只要运行 snakemake,它会自动生成所有样本的质控报告。

3.7 全局配置与函数

你可以在 Snakefile 顶部定义 Python 变量、函数,甚至读取配置文件。

import pandas as pd
SAMPLES = pd.read_csv("samples.csv")["sample_id"].tolist()

rule all:
    input:
        expand("{sample}_fastqc.html", sample=SAMPLES)

关键点:Snakefile 本质是 Python,所以能用 os.path.join、glob、字典 等任何 Python 特性。

3.8 避坑指南:我踩过的三个坑

  • 路径问题:我曾经在 shell 命令里写相对路径 fastqc {input},结果 Snakemake 的工作目录不是你想象的目录。后来我改用 {input} 的绝对路径,或者用 workdir: 指令固定目录。
  • 通配符冲突:如果两个规则使用同样的通配符名字但不同格式,Snakemake 会报错。比如 {sample}.txt 和 {sample}.bam 没问题,但 {sample}_a 和 {sample}_b 可能出歧义。
  • 漏写 output:有一次我忘了把日志文件写到 output 里,结果 Snakemake 觉得任务没完成,每次都要重跑。

警告:通配符不能与 Python 的关键字重名(比如 input、output)。

3.9 本章知识体系 SVG

Snakefile 语法核心结构 Snakefile rule rule rule rule input output params shell/run 通配符 {sample} 依赖推断 DAG 有向无环图 并行执行 自动构建执行计划 → 生成所有目标文件

这张图帮你理清层次:Snakefile 包含多条规则,每条规则有输入、输出、参数、执行方式,规则通过通配符和依赖推断自动形成 DAG,最后 Snakemake 按照 DAG 并行执行。

3.10 趁热练手:一个完整的小例子

假设你有三个 fastq 文件,要做质控:

# Snakefile
SAMPLES = ["sample1", "sample2", "sample3"]

rule all:
    input:
        expand("{s}_fastqc.html", s=SAMPLES)

rule fastqc:
    input:
        "{sample}.fastq"
    output:
        "{sample}_fastqc.html",
        "{sample}_fastqc.zip"
    shell:
        "fastqc -o . {input}"

运行 snakemake -j 3,三个样本同时跑。
是不是比写三个脚本清爽多了?

试一下:把 -j 3 改成 -j 1,看看顺序执行的日志。

3.11 小结

Snakefile 的核心就这些:

  • 规则(rule)是基本单元
  • 通配符让规则一次定义多次使用
  • 依赖自动推导,不用手动控制顺序
  • 可以混用 Python 增强灵活性

把这些记牢,后面学高级功能就轻松了。我当年就是靠这三个概念啃完整个项目的。