3. 流程脚本基础结构
Nextflow 脚本(通常命名为 main.nf)本质上是一段 Groovy DSL 代码,但它把“流程编排”这件事抽象成了三个核心组件:process 定义“做什么”,channel 定义“数据从哪来、往哪去”,workflow 定义“按什么顺序、以什么依赖关系把它们串起来”。理解这三者的职责边界,是读懂任何 Nextflow 流程、也是自己动手写流程的第一步。
一、脚本骨架:三段式结构
一个规范的 Nextflow 脚本通常按以下顺序组织:
#!/usr/bin/env nextflow
// 1. 参数声明(可选,但推荐)
params.reads = "$projectDir/data/*.fastq.gz"
params.outdir = "results"
// 2. process 定义(可多个)
process FASTQC {
...
}
process TRIMMOMATIC {
...
}
// 3. workflow 定义(入口)
workflow {
...
}
其中 params 是全局参数容器,通过命令行 --reads 覆盖;process 块可以出现多次,每个块是一个独立可执行单元;workflow 块是执行入口,Nextflow 运行时从它开始解析依赖并调度任务。注意:process 只是定义,不会自动执行,只有被 workflow 引用后才会进入执行图。
二、process:最小执行单元
process 是 Nextflow 中真正“干活”的地方。它由若干指令(directive)和脚本块(script)组成,典型结构如下:
process FASTQC {
tag "$sample_id"
publishDir "${params.outdir}/fastqc", mode: 'copy'
input:
tuple val(sample_id), path(reads)
output:
path("*.html"), emit: html
path("*.zip"), emit: zip
script:
"""
fastqc -t 4 -o . ${reads}
"""
}
关键点:
- input 声明输入通道,
tuple val(sample_id), path(reads)表示每个任务接收一个样本 ID 和对应文件路径; - output 声明输出通道,
emit给输出命名,便于在 workflow 中引用; - script 是实际执行的 shell 命令,
${reads}会被 Nextflow 替换为真实路径; - publishDir 把结果复制/链接到指定目录,
mode: 'copy'表示复制而非软链。
process 的输入输出都是 channel,因此它天然支持并行:如果输入通道里有 10 个样本,Nextflow 会自动启动 10 个任务实例,无需手写循环。
三、channel:数据流的载体
channel 是 Nextflow 的数据管道,分为两类:
- Queue channel:有顺序、可被多次消费,适合流式数据,如
Channel.fromPath(); - Value channel:单值、可被多次引用,适合参数、参考基因组等,如
Channel.value()。
常见创建方式:
// 从文件路径创建
reads_ch = Channel.fromPath("$params.reads")
// 从列表创建
samples_ch = Channel.fromList(["S1", "S2", "S3"])
// 组合成 tuple
input_ch = samples_ch.combine(reads_ch)
channel 的操作符(operator)是 Nextflow 的精髓,例如 map、filter、combine、groupTuple、collect 等,它们决定了数据如何被转换和分发。理解 channel 的“惰性求值”特性很重要:channel 在未被消费前不会真正产生数据。
四、workflow:编排与执行入口
workflow 块是脚本的执行入口,它负责把 process 和 channel 连接起来:
workflow {
reads_ch = Channel.fromPath(params.reads)
FASTQC(reads_ch)
TRIMMOMATIC(reads_ch)
FASTQC(TRIMMOMATIC.out.trimmed)
}
这里 FASTQC(reads_ch) 表示把 reads_ch 作为输入调用 FASTQC process;TRIMMOMATIC.out.trimmed 是 TRIMMOMATIC 的输出通道,作为 FASTQC 的输入,从而形成依赖链。Nextflow 会自动构建 DAG(有向无环图),并按依赖顺序调度任务。
从 Nextflow 22.10 起,推荐使用 workflow { } 作为主入口,并支持命名子工作流:
workflow QC {
take: reads
main:
FASTQC(reads)
emit: FASTQC.out.html
}
workflow {
reads_ch = Channel.fromPath(params.reads)
QC(reads_ch)
}
命名工作流让复杂流程可以模块化复用,take 声明输入,main 是主体,emit 声明输出,这与 process 的 input/output 设计思路一致。
五、执行入口与运行方式
脚本写好后,用 nextflow run main.nf 启动。Nextflow 会:
- 解析脚本,构建 process 与 channel 的依赖图;
- 根据
-profile或nextflow.config选择执行器(local、slurm、aws 等); - 按 DAG 调度任务,自动处理并行、重试、缓存;
- 把日志写入
.nextflow.log,结果按publishDir输出。
常用命令示例:
nextflow run main.nf -profile docker --reads 'data/*.fastq.gz' --outdir results
nextflow run main.nf -resume # 复用缓存,跳过已完成步骤
-dump-channels 查看通道内容,用 -with-trace 生成执行报告,快速定位是 channel 为空还是 process 报错。
六、三大组件关系图解
下图把 process、channel、workflow 三者的职责与数据流向放在同一张图中:channel 是数据源与管道,process 是执行节点,workflow 是编排层,三者共同构成 Nextflow 脚本的骨架。
七、常见误区与检查清单
- process 不写 input/output 直接跑:会导致无法并行、无法复用,且 workflow 无法引用其输出;
- 把 channel 当普通变量:channel 只能被消费一次(Queue channel),重复使用需用
into或重新创建; - workflow 里写 shell 命令:workflow 只做编排,实际命令必须放在 process 的 script 中;
- 忘记 emit 命名:多输出时无法在 workflow 中区分,建议每个 output 都加
emit; - 混淆 params 与 channel:params 是配置值,channel 是数据流,二者用途不同。
nextflow run main.nf。掌握这三者的职责与连接方式,就掌握了 Nextflow 流程工程的地基。