第2章 规则(Rule)基础:规则定义、输入输出、运行命令

Snakemake 的核心就是规则。说白了,每条规则告诉系统:你要用哪些输入文件,生成哪些输出文件,中间跑什么命令。
我刚开始接触时,觉得这和 Makefile 差不多嘛,但用着用着就发现,它比 Makefile 聪明太多了。

2.1 一条规则长什么样

一条完整的规则,至少包含三样东西:规则名字、输入文件、输出文件。
如果再给个运行命令,它就能干活了。

rule fastqc:
    input:
        "data/sample1.fastq"
    output:
        "results/fastqc/sample1_fastqc.html"
    shell:
        "fastqc {input} -o results/fastqc/"

这里 rule fastqc 是规则名,input 和 output 分别指定了输入输出路径,shell 里写的是要执行的命令。{input} 和 {output} 是 Snakemake 自动解析的占位符,会自动替换成实际文件路径。

我的习惯:规则名尽量用动词+对象,比如 run_fastqc、trim_adapters,这样一看就知道它在干啥。

2.2 输入输出:不仅仅是路径

input 和 output 支持多种写法。最常见的自然是字符串路径,但遇到多个文件时,就得用列表或字典了。

rule multiqc:
    input:
        "results/fastqc/sample1_fastqc.zip",
        "results/fastqc/sample2_fastqc.zip",
        "results/fastqc/sample3_fastqc.zip"
    output:
        "results/multiqc/multiqc_report.html"
    shell:
        "multiqc {input} -o results/multiqc/"

如果输入文件有几百个,手动写列表就太傻了。这时候我会用 expand() 函数批量生成路径。

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

rule multiqc:
    input:
        expand("results/fastqc/{sample}_fastqc.zip", sample=SAMPLES)
    output:
        "results/multiqc/multiqc_report.html"
    shell:
        "multiqc {input} -o results/multiqc/"

expand() 会遍历所有样本名,拼接出完整列表。后面讲到通配符时,你会发现它和通配符是一对好搭档。

2.3 运行命令的三种方式

Snakemake 提供三种方式来执行规则里的计算:

  • shell:直接写 shell 命令,适合快速调用现成工具
  • run:用 Python 代码块,适合需要逻辑处理的场景
  • script:调用外部脚本(R、Python 等),适合已经写好的分析脚本

看看它们的具体用法:

# shell 方式 - 调用外部工具
rule cutadapt:
    input:
        "data/{sample}.fastq"
    output:
        "results/trimmed/{sample}.fastq"
    shell:
        "cutadapt -a AGATCGGAAGAGC -o {output} {input}"

# run 方式 - Python 内联处理
rule count_reads:
    input:
        "data/{sample}.fastq"
    output:
        "results/counts/{sample}.txt"
    run:
        total = 0
        with open(input[0]) as f:
            for i, line in enumerate(f):
                if i % 4 == 1:
                    total += 1
        with open(output[0], "w") as out:
            out.write(f"{total}\n")

# script 方式 - 调用外部 R 脚本
rule plot_distribution:
    input:
        "results/counts/{sample}.txt"
    output:
        "results/plots/{sample}_dist.pdf"
    script:
        "scripts/plot_dist.R"
我曾经踩过一个坑:用 run 时忘记写 input 和 output 的索引。注意,input 和 output 在 run 块里是列表或字典,要用 input[0] 或 output["key"] 来取具体文件。直接写 input 会报错!

2.4 规则的执行顺序与依赖

Snakemake 会自动分析规则之间的输入输出依赖,确定执行顺序。举个例子:

rule fastqc:
    input:
        "data/{sample}.fastq"
    output:
        "results/fastqc/{sample}_fastqc.html"
    shell:
        "fastqc {input} -o results/fastqc/"

rule multiqc:
    input:
        expand("results/fastqc/{sample}_fastqc.html", sample=SAMPLES)
    output:
        "results/multiqc/multiqc_report.html"
    shell:
        "multiqc {input} -o results/multiqc/"

Snakemake 看到 multiqc 需要 fastqc 的输出,就会先执行所有 fastqc 任务,再执行 multiqc。你不需要手动排序,它自己会推算。

但有一点要留心:如果两个规则的输出文件名相同,Snakemake 会报错。所以输出路径一定要有区分度,我习惯用 results/工具名/样本名.后缀 这种模式。

2.5 规则结构全景图

下面这张图把规则的核心元素串在了一起,你看看就明白了:

规则(Rule)结构一览 rule 规则名 input: 输入文件 output: 输出文件 运行命令 shell / run / script 关键点 依赖推导:Snakemake 根据 output → input 自动排序执行 通配符 {sample} 让一条规则可以处理所有样本

一句话总结:规则定义输入输出,Snakemake 自己跟踪依赖,你只管写命令。这就是它比脚本批处理的高明之处。

2.6 常见小坑与习惯

问题 表现 解决
输出路径写错 规则永远不执行,说输出已经存在 检查 output 里的目录是否已创建,Snakemake 不会自动建目录
通配符没用对 报错 "Wildcards in input not in output" 所有出现在 input 里的通配符,output 里必须有
shell 命令太长 可读性差,不好维护 用反斜杠换行,或者写到单独的脚本里用 script 调用

我个人习惯,每写一条新规则都先跑 snakemake -n 做一次 dry-run,看看 Snakemake 解析出来要执行哪些步骤。能提前发现输入输出不匹配的问题。

规则的基础就这些。你想想看,当输入输出定义清楚后,Snakemake 就能自动帮你管理数据流和任务并行。后面章节我们会往规则里加入通配符、线程、资源控制,让它真正强大起来。不过,先把今天这三板斧练熟——规则名、输入输出、运行命令——你已经能写出可用的工作流了。