PAYATHON 2026

Slurm 脚本的 #SBATCH 指令可以使用预先定义的变量吗?

数据小陈

不能按示例中的方式使用变量。Slurm 在提交阶段解析 #SBATCH,不会执行 Bash 变量展开或命令替换。通常只需在调用 sbatch 时传入计算好的参数,无须动态生成整个脚本。

为什么原来的写法无效?

原来的写法有两个问题:

  • NODE_NUM=... 是一条既非注释也非空白的语句。Slurm 读取到它后,会停止处理后面的 #SBATCH 指令,因此示例中的 --output 不会生效。
  • 即使把变量赋值移到所有 #SBATCH 指令之后,指令里的 ${NODE_NUM} 仍会被当作字面文本,不会展开。

这些规则见 Slurm 官方 sbatch 文档。通过 export 导出变量,也不能让 #SBATCH 自动进行 Bash 展开。

变量在提交时就能确定:通过命令行传入

如果需要的是提交机器的 $HOSTNAME 的最后一个字符,可以在提交用的 Bash 中计算:

mkdir -p logs
NODE_NUM=$(printf '%s' "$HOSTNAME" | tail -c 1)
sbatch --output="logs/job_notebook_${NODE_NUM}.out" job.sbatch

提交端的 Bash 会先展开 ${NODE_NUM},再把完整路径传给 sbatch。job.sbatch 可以保持为普通作业脚本:

#!/bin/bash
#SBATCH --job-name=notebook

# 在这里放实际运行的命令

示例假定 Bash 位于 /bin/bash,且作业运行节点能够访问对应的日志目录。使用相对路径时,需要考虑作业工作目录以及是否设置了 --chdir。日志目录应在作业启动前存在,不能依赖脚本正文为 --output 创建父目录。

这里得到的是提交机器的信息,通常是登录节点的信息,不能据此判断作业最终运行在哪个计算节点。

需要运行节点信息:使用占位符或运行时重定向

如果只需要把节点名称放进文件名,可以直接使用 Slurm 的文件名占位符:

#!/bin/bash
#SBATCH --output=logs/job_notebook_%N_%j.out

# 在这里放实际运行的命令

其中 %N 表示短主机名,%j 表示作业 ID。%n 表示节点在当前作业中的相对编号,不等于主机名末尾的数字。这些定义见 官方文件名模式说明。批处理脚本的输出也不能自动代表所有节点上的任务输出;各任务的输出还取决于任务的启动方式及其输出设置。

如果必须取运行节点主机名的最后一个字符,可以在脚本开始执行后计算,再重定向后续输出:

#!/bin/bash
#SBATCH --output=job_notebook_%j.bootstrap.out

mkdir -p logs || exit 1
NODE_NUM=$(hostname -s | tail -c 1)
exec >"logs/job_notebook_${NODE_NUM}_${SLURM_JOB_ID}.out" 2>&1

# 后续命令的标准输出和标准错误写入上述文件

这种方式不会修改 Slurm 的 --output 设置,重定向之前的输出仍会进入初始日志。取最后一个字符也不等于提取完整节点编号,例如 node12 得到的是 2。

动态生成脚本同样可行,但生成结果中的 #SBATCH 参数必须已经是确定的文本。如果值只有作业启动后才能知道,提前生成脚本也无法获得该值。

备注:内容仅供参考。