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 会:

  1. 解析脚本,构建 process 与 channel 的依赖图;
  2. 根据 -profile 或 nextflow.config 选择执行器(local、slurm、aws 等);
  3. 按 DAG 调度任务,自动处理并行、重试、缓存;
  4. 把日志写入 .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 脚本的骨架。

Nextflow 脚本三大组件与数据流 workflow(编排层 / 执行入口) 职责:声明依赖关系、调用 process、连接 channel,构建 DAG 入口:workflow { ... } 或命名子工作流 workflow NAME { take / main / emit } channel(数据层) Queue channel:fromPath / fromFilePairs Value channel:value / fromList 操作符:map / filter / combine / collect 特性:惰性求值,被消费时才产生数据 process(执行层) input:tuple val(id), path(reads) output:path("*.html"), emit: html script:实际 shell 命令 指令:tag / publishDir / cpus / memory 输入 输出 执行结果与调度 DAG 调度 → 并行任务 → publishDir 输出 → .nextflow.log 日志 → -resume 缓存复用 process 定义“做什么”,channel 定义“数据流”,workflow 定义“怎么串” 三者共同构成 main.nf 的骨架,nextflow run 从 workflow 入口开始解析执行

七、常见误区与检查清单

  • process 不写 input/output 直接跑:会导致无法并行、无法复用,且 workflow 无法引用其输出;
  • 把 channel 当普通变量:channel 只能被消费一次(Queue channel),重复使用需用 into 或重新创建;
  • workflow 里写 shell 命令:workflow 只做编排,实际命令必须放在 process 的 script 中;
  • 忘记 emit 命名:多输出时无法在 workflow 中区分,建议每个 output 都加 emit;
  • 混淆 params 与 channel:params 是配置值,channel 是数据流,二者用途不同。
本节要点回顾:process 是执行单元,channel 是数据管道,workflow 是编排入口。脚本骨架按“参数 → process → workflow”组织,执行入口是 workflow 块,运行命令为 nextflow run main.nf。掌握这三者的职责与连接方式,就掌握了 Nextflow 流程工程的地基。