
Snakemake 默认仅锁定显式声明的输出文件,不自动递归锁定目录内所有文件;但可通过将目录声明为 output: directory("path/") 实现目录级排他访问——需注意该操作会触发 Snakemake 在执行前清空整个目录,使用时须谨慎评估数据安全性。
snakemake 默认仅锁定显式声明的输出文件,不自动递归锁定目录内所有文件;但可通过将目录声明为 output: directory("path/") 实现目录级排他访问——需注意该操作会触发 snakemake 在执行前清空整个目录,使用时须谨慎评估数据安全性。
在并行工作流中,当多个规则需读写同一动态生成文件集所在的目录(如临时缓存目录、中间结果目录)时,仅依赖文件级锁无法保证一致性——因为文件名在运行时才确定,无法提前在 output 中静态枚举。此时,目录级锁成为关键的并发控制手段。
Snakemake 支持通过 directory() 函数将目录声明为输出目标,从而将其纳入资源锁定机制:
rule process_in_shared_dir:
output:
directory("results/cache/")
shell:
"mkdir -p {output} && "
"some_tool --input data.txt --output-dir {output} && "
"touch {output}/_completed"✅ 生效机制:
- Snakemake 会为该目录创建 .snakemake/locks/ 下的独占锁文件(如 results/cache/.snakemake.lock);
- 其他尝试声明相同 directory(...) 为输出的规则将被阻塞,直至当前作业释放锁;
- 锁作用于目录路径本身,而非其内容,因此能有效防止多进程同时进入同一目录进行读写。
⚠️ 关键注意事项:
- 自动清理风险:Snakemake 在作业启动前会递归删除该目录下所有内容(包括子目录、隐藏文件及已有中间结果)。若目录中存在非 Snakemake 生成但需复用的文件,此行为将导致数据丢失;
- 非递归锁定:锁仅保障“对该目录的写入权”,不阻止进程访问其子目录中的未声明文件(除非子目录也被单独声明为 directory() 输出);
-
替代方案建议:
- 若需保留历史文件,改用唯一命名的子目录(如 output: directory("results/cache/{wildcards.sample}/"));
- 对高敏感场景,结合外部锁工具(如 flock)或数据库事务实现更细粒度控制;
- 使用 shadow: "minimal" 或自定义 run 块绕过默认清理逻辑(需手动管理目录状态)。
综上,directory() 是实现目录级并发安全的有效原生方案,但必须明确其“先清空、后执行”的语义边界。合理设计输出目录结构 + 显式声明 + 充分测试,是构建鲁棒 Snakemake 并行流水线的关键实践。










