2. Nextflow入门:安装、基础概念、第一个Hello World流程

好,我们正式开始动手。这一章的目标很简单——让你在本地跑通一个Nextflow流程,哪怕它只是打印个"Hello World"。但别小看这步,我自己的经验是,很多人卡就卡在环境配置和基础概念上。先把地基打牢,后面复杂流程才能跑得稳。

2.1 安装Nextflow

Nextflow是用Groovy写的,但它跑在Java虚拟机(JVM)上。所以你只需要两样东西:Java 11+和网络。

2.1.1 环境要求

组件最低版本推荐版本
Java (JDK/JRE)1117 或 21 LTS
curl / wget任意最新
操作系统Linux / macOS / Windows(WSL)Ubuntu 20.04+ / macOS 12+

注意:不要用Java 8。Nextflow从20.x版本开始就不再支持Java 8。我曾经看到有人装了半天发现跑不起来,一查是Java版本太老——白白浪费半小时。

2.1.2 安装步骤

我习惯用curl一行搞定:

curl -s https://get.nextflow.io | bash

然后把它加到PATH里:

chmod +x nextflow
sudo mv nextflow /usr/local/bin/

验证安装:

nextflow -version

      N E X T F L O W
      version 24.04.2 build 5890
      created 10-06-2024 10:48 UTC
      cite doi:10.1038/nbt.3820

小技巧:如果你在公司内网或者代理后面,Nextflow下载组件经常超时。我个人习惯设置环境变量NXF_OPTS='-Dhttp.proxyHost=... -Dhttp.proxyPort=...'。另外,第一次运行任何流程时,Nextflow会自动下载运行时组件,等几分钟就好。

2.2 核心概念——一句话解释

开始写流程前,先把几个关键概念理清楚。别急,我用最直白的方式说。

  • Process(进程):流程里最小的执行单元。每个Process干一件具体的事,比如比对、排序、变异检测。你可以把它理解成一个函数——输入、输出、执行命令。
  • Channel(通道):数据流动的管道。Process从Channel读输入,往Channel写输出。Channel就像自来水管,数据就是水。
  • Workflow(工作流):定义Process之间的连接关系。哪个Process先跑,哪个后跑,数据怎么传递,都在这里说清楚。
  • DSL2:Nextflow从22版本起主推的语法风格。DSL2让流程模块化,每个Process和Workflow都更清晰。

举个例子,在真实项目中,我经常看到新手把Process写得又大又乱,恨不得一个Process把比对、排序、去重全干了。这不好。保持Process小、单一职责,调试的时候你才知道哪里炸了。

2.3 第一个Hello World流程

说了这么多,直接上手。我们要写一个最简单的流程:定义两个Process,一个生成"Hello",一个生成"World",最后合并输出。

2.3.1 创建项目结构

mkdir hello-nextflow
cd hello-nextflow
touch main.nf

所有代码写在main.nf里。我先放完整代码,然后逐行解释。

2.3.2 完整代码

#!/usr/bin/env nextflow
nextflow.enable.dsl=2

process sayHello {
    output:
        stdout()

    script:
    """
    echo "Hello"
    """
}

process sayWorld {
    output:
        stdout()

    script:
    """
    echo "World"
    """
}

workflow {
    hello_ch = sayHello()
    world_ch = sayWorld()
    greeting_ch = hello_ch.concat(world_ch).collect()

    greeting_ch.view { it -> "${it[0]} ${it[1]}!" }
}

2.3.3 逐行拆解

第一行#!/usr/bin/env nextflow是Shebang,告诉你用Nextflow解释器跑这个文件。不过不重要,关键是第二行:

nextflow.enable.dsl=2

这行开启了DSL2模式。如果你不写,Nextflow会用旧语法(DSL1)。我个人强烈建议直接用DSL2,新项目没人再用DSL1了。

接下来是两个Process。每个Process有input:(可省略)、output:、script:三个块。这里我们没有输入,只有标准输出。注意script:块里可以写任意shell命令——Nextflow会把这段内容生成一个临时脚本去执行。

最后是workflow { }块。DSL2里,这里就是流程编排的地方。我们调用了两个Process,拿它们的输出通道(channel);然后用concat和collect组合成一个包含两个元素的列表;最后用view操作打印结果。

2.3.4 运行看看

nextflow run main.nf

输出大概长这样:

N E X T F L O W  ~  version 24.04.2
Launching `main.nf` [insane_minsky] DSL2 - revision: 123abc
executor >  local (2)
[19/7a8b1c] sayHello [100%] 1 of 1
[6e/2f3b4a] sayWorld [100%] 1 of 1
Hello World!

看到最后一行"Hello World!"了吗?这就是我们第一个工作流的输出。

关键点:Nextflow会自动为每个Process创建一个独立的工作目录(work/下面那些哈希命名的文件夹),所有临时文件和日志都在那里。如果你需要调试,直接进对应工作目录看.command.sh和.command.out即可。

2.4 知识体系地图

为了让你对本章知识有个整体印象,我手绘了一张图。它展示了Nextflow核心组件、它们之间的关系和我们的Hello World流程对应位置。

Nextflow 入门知识体系与 Hello World 流程结构 Process (进程) Channel (通道) Workflow (编排) input / output script (shell) queue / value channel operator (concat, collect) Hello World 流程实例 sayHello sayWorld concat + collect view 输出 "Hello World!" 提示:每个Process运行在独立工作目录,过程可复现

2.5 常见陷阱和最佳实践

写完第一个流程,来看看新手经常踩的坑。

坑1:忘记DSL2声明。如果不写nextflow.enable.dsl=2,workflow { }块根本不会被识别,报错说"未预期的符号"。我刚开始转DSL2时也栽过。

坑2:Process命名冲突。多个Process不能重名。此外,Process名字里不要有特殊符号,用驼峰或下划线就好。

好习惯:每个Process只做一件事。比如比对和排序分开,虽然可以用管道连起来,但分开写方便后续替换工具、并行执行。像我们在Hello World里,sayHello和sayWorld就是两个独立的Process,互不干扰。

2.6 本章回顾

嗯,到这里你就完成了Nextflow的入门。我们干了三件事:

  1. 装了Java和Nextflow,跑通了nextflow -version。
  2. 理解了Process、Channel、Workflow和DSL2的含义——说白了就是「谁干活、数据怎么流、谁指挥谁」。
  3. 写了一个真正的Hello World流程,看到输出那一刻,是不是挺有成就感的?