2. Nextflow入门:安装、基础概念、第一个Hello World流程
好,我们正式开始动手。这一章的目标很简单——让你在本地跑通一个Nextflow流程,哪怕它只是打印个"Hello World"。但别小看这步,我自己的经验是,很多人卡就卡在环境配置和基础概念上。先把地基打牢,后面复杂流程才能跑得稳。
2.1 安装Nextflow
Nextflow是用Groovy写的,但它跑在Java虚拟机(JVM)上。所以你只需要两样东西:Java 11+和网络。
2.1.1 环境要求
| 组件 | 最低版本 | 推荐版本 |
|---|---|---|
| Java (JDK/JRE) | 11 | 17 或 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流程对应位置。
2.5 常见陷阱和最佳实践
写完第一个流程,来看看新手经常踩的坑。
坑1:忘记DSL2声明。如果不写nextflow.enable.dsl=2,workflow { }块根本不会被识别,报错说"未预期的符号"。我刚开始转DSL2时也栽过。
坑2:Process命名冲突。多个Process不能重名。此外,Process名字里不要有特殊符号,用驼峰或下划线就好。
好习惯:每个Process只做一件事。比如比对和排序分开,虽然可以用管道连起来,但分开写方便后续替换工具、并行执行。像我们在Hello World里,sayHello和sayWorld就是两个独立的Process,互不干扰。
2.6 本章回顾
嗯,到这里你就完成了Nextflow的入门。我们干了三件事:
- 装了Java和Nextflow,跑通了
nextflow -version。 - 理解了Process、Channel、Workflow和DSL2的含义——说白了就是「谁干活、数据怎么流、谁指挥谁」。
- 写了一个真正的Hello World流程,看到输出那一刻,是不是挺有成就感的?