如果我的脚本正在由SLURM执行,如何获取另一个bash脚本的源代码?

如果我的脚本正在由SLURM执行,如何获取另一个bash脚本的源代码?,bash,slurm,Bash,Slurm,我有在集群上运行并行程序的脚本。我用通常的命令运行它: sbatch-p PARTITION-t TIME-N NODES/full/path/to/my/script.sh PARAMETERS-LIST 在script.sh中,我需要另一个bash脚本(位于script.sh所在的同一目录中)来加载一些例程/变量。对于在本地计算机上执行的常用脚本,我使用以下命令: SCRIPTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null &

我有在集群上运行并行程序的脚本。我用通常的命令运行它:

sbatch-p PARTITION-t TIME-N NODES/full/path/to/my/script.sh PARAMETERS-LIST

script.sh
中,我需要另一个bash脚本(位于
script.sh
所在的同一目录中)来加载一些例程/变量。对于在本地计算机上执行的常用脚本,我使用以下命令:

SCRIPTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd )"
source "$SCRIPTDIR/funcs.sh"
print_header "Some text"
而且效果很好。但是,在集群上,这不起作用,我得到以下错误(仅举个例子):

似乎SLURM创建了自己要提交的脚本副本,因此我无法获取任何本地脚本/文件


在这种情况下可以做些什么?如果我可以避免在脚本中硬编码绝对路径,那就太好了…

您可以通过以下方法更改
script.sh的工作目录:

sbatch -p PARTITION -t TIME -N NODES -D /full/path/to/my/ /full/path/to/my/script.sh PARAMETERS-LIST

然后在脚本中,您只需执行
source“funcs.sh”

即可。问题在于,如果您只是从桌面命令提示符运行sbatch shell脚本,那么sbatch shell脚本的位置就与在节点上运行slurmstepd
不同。之所以会出现这种情况,是因为sbatch使用Slurm的快速分层网络拓扑机制,将脚本物理复制到分配的每个头节点,并从那里运行脚本。这样做的最终效果是,当当前目录传播到脚本执行环境时,脚本的路径不同(并且在不同节点上可能不同)。让我用你的例子来解释

发生了什么事? 当然,您包含的脚本必须被视为文件系统树中同一位置的同一文件(通常在NFS装载上)。在本例中,我假设您的用户名是
bob
(因为它肯定不是),并且您的主目录
/home/bob
是从每个节点上的NFS导出以及您自己的机器上装载的

阅读您的代码,我知道主脚本
script.sh
和源文件
funcs.sh
位于同一目录中。为了简单起见,让我们将它们直接放在您的主目录中:

$ pwd
/home/bob
$ ls
script.sh funcs.sh
$ ../bob/script.sh PARAMETERS-LIST
. utils/parse_options.sh
$ pwd
/home/bob/kaldi/egs/fisher_english/s5
$ utils/nnet/some_utility_script.sh  # This works.
$ cd utils/nnet
$ ./some_utility_script.sh           # This fails, by design.
我还要修改
script.sh
,如下所示:我将添加
pwd
行以查看我们的位置,并删除失败的
内置代码之外的其余部分,因为这无论如何都是无关的

#!/bin/bash
pwd
SCRIPTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd )"
本地跑步

无论哪个目录是当前目录,都是不相关的,因此,让我们通过指定脚本的相对路径使测试稍微复杂一点,即使它位于当前目录中:

$ pwd
/home/bob
$ ls
script.sh funcs.sh
$ ../bob/script.sh PARAMETERS-LIST
. utils/parse_options.sh
$ pwd
/home/bob/kaldi/egs/fisher_english/s5
$ utils/nnet/some_utility_script.sh  # This works.
$ cd utils/nnet
$ ./some_utility_script.sh           # This fails, by design.
在这种情况下,bash将按如下方式对脚本进行评估(逐步执行,命令stdout、变量扩展结果或变量赋值显示在以
=>
为前缀的每一行)

pwd
 => '/home/bob'

# Evaluate: SCRIPTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd )"
${BASH_SOURCE[0]}
 => '../bob/script.sh'
dirname '../bob/script.sh'
 => '../bob'
cd '../bob'
 => Success, $? is 0
pwd
 => '/home/bob'
SCRIPTDIR='/home/bob'

# Evaluate: source "$SCRIPTDIR/funcs.sh"
$SCRIPTDIR
 => '/home/bob'
source '/home/bob/funcs.sh'
 => (Successfully sourced)
在这里,您打算从
script.sh
所在的同一目录中寻找
funcs.sh
的行为运行良好

慢跑

Slurm将
script.sh
复制到节点上的spool目录,然后从那里执行。如果指定
-D
切换到sbatch,则当前目录将设置为该目录(如果失败,则设置为
$TMPDIR
的值;如果失败,则设置为
/tmp
)。如果未指定
-D
,则将使用当前目录。现在,假设节点上安装了
/home/bob
,并且您只需提交脚本而不使用
-D

$ sbatch -N1 ./script.sh PARAMETERS-LIST
Slurm为您分配一台节点计算机,将脚本的内容复制到本地文件中(在您的示例中,该文件名为
/var/tmp/slurmd/job1043319/Slurm\u script
),将当前目录设置为
/home/bob
,并执行脚本文件
/var/tmp/slurmd/job1043319/slurm\u script
。我想您已经理解了将要发生的事情

pwd
 => '/home/bob'

# Evaluate: SCRIPTDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd )"
${BASH_SOURCE[0]}
 => '/var/tmp/slurmd/job1043319/slurm_script'
dirname '/var/tmp/slurmd/job1043319/slurm_script'
 => '/var/tmp/slurmd/job1043319'
cd '../bob'
 => Success, $? is 0
pwd
 => '/home/bob'
SCRIPTDIR='/var/tmp/slurmd/job1043319'
我想我们应该到此为止。您已经看到主脚本及其源文件在同一目录中的假定不变被违反。您的脚本依赖于此不变,因此会中断

那么我该如何解决这个问题呢? 这取决于你的要求。你没有说明任何问题,但我可以给出一些建议,这些建议可能与你的目标在不同程度上保持一致。我的回答可能有积极的一面,对更广泛的SO受众有用

选项1。与您自己(以及脚本的其他用户,如果有的话)签订绑定协议,以便始终在特定目录中启动脚本

实际上,这是一个著名的语音识别工具包Kaldi所采用的方法:任何脚本,任何运行的命令,都必须从

如果这种方法是可行的,那么任何源代码都可以从当前目录(和/或其下的已知路径)中获取;或者

在自身与主实验目录软链接的目录中:

$ pwd
/home/bob
$ ls
script.sh funcs.sh
$ ../bob/script.sh PARAMETERS-LIST
. utils/parse_options.sh
$ pwd
/home/bob/kaldi/egs/fisher_english/s5
$ utils/nnet/some_utility_script.sh  # This works.
$ cd utils/nnet
$ ./some_utility_script.sh           # This fails, by design.
这些
语句都不能在从非常规目录调用的任何脚本中工作:

$ pwd
/home/bob
$ ls
script.sh funcs.sh
$ ../bob/script.sh PARAMETERS-LIST
. utils/parse_options.sh
$ pwd
/home/bob/kaldi/egs/fisher_english/s5
$ utils/nnet/some_utility_script.sh  # This works.
$ cd utils/nnet
$ ./some_utility_script.sh           # This fails, by design.
优点:可读代码。当您有3000个bash文件,共600000行代码时,正如我们在这里所做的那样,这一点很重要。
优点:该代码与HPC群集无关,几乎所有脚本都可以在您的计算机上运行,可以使用本地多核并行,也可以不使用本地多核并行,也可以使用简单的ssh将计算分散到小型群集上,或者使用Slurm、PBS、Sun GridEngine等等。
缺点:用户必须了解该要求

为了评估这种方法的底线,如果您有大量相互依赖的脚本文件,并且您的工具包很复杂,并且自然具有中等或高的学习能力,那么利大于弊