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 参数必须已经是确定的文本。如果值只有作业启动后才能知道,提前生成脚本也无法获得该值。
备注:内容仅供参考。
