在Python脚本上应用源代码以导出环境变量?

问题描述

上下文:

我正在与一些开发人员合作,其中一些使用OSX,一些使用Linux。我们使用bash脚本集合来加载环境变量,构建docker映像等。OSX用户使用zsh而不是bash,因此某些技巧(特别是我们如何获取运行脚本的目录)不兼容。在Linux(而不是OSX)上有效的是:

$ cat ./scripts/env_script.sh
THIS_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
# ... more variables ...
$ source ./scripts/env_script.sh
$ echo $THIS_DIR
# the absolute path to the scripts directory

一些粗略的测试表明这在OSX上不起作用,大概是因为BASH_SOURCE在zsh中不起作用。很好

目标:

我们都在使用Python,我们想用Python替换bash脚本。 在Python中,相同的模式非常容易。

from pathlib import Path
THIS_DIR = str(Path(__file__).parent)

不是最干净的,但是可以在任何地方使用。理想情况下,我们可以运行此程序并与调用脚本的shell共享THIS_DIR。像

$ <answer to my question> ./scripts/env_script.py
$ echo $THIS_DIR
# absolute path to the scripts directory

问题:

假设我们将./scripts/env_script.sh重写为./scripts/env_script.py并收集了一个我们希望与调用环境共享的变量列表。 也就是说,我们在上面编写了Python脚本。

是否可以做类似于source ./scripts/env_script.py的事情,以便我们可以将Python脚本中的变量导出到调用shell中?

我尝试过的事情:

  • 我发现了一个潜在的重复here,尽管答案基本上是“ Python在子进程中运行,只有您的shell Python”才有意义,但源代码才有效”回答问题。
  • 如果没有很好的方法来交换这些变量,那么另一种选择基本上是让Python脚本将变量吐到stdout,然后在我的shell中进行评估。即eval $(./script/env_script.py)。我想这可能行得通,但至少因为我一直被教导eval是邪恶的,所以感觉到是错误的。

解决方法

我在第二个解决方案上花了更多时间,因为我可以想象它可以工作(尽管我对此不满意)。基本上,我将./scripts/env_script.py重写为

FOO='whatever'
THIS_DIR=str(Path(__file__).parent)
# and so on...

# prints all "normal" variables to stdout
def_vars = list(locals().items())
for name,val in def_vars:
    if name.startswith("_"):
        continue
    print(f'{name}="{val}"')

因此正常运行它将输出

$ ./scripts/env_script.py
FOO="whatever"
THIS_DIR="..."
...
$ echo $FOO
# nothing

然后,要将这些变量塞入调用外壳程序中,我们用eval将其包装起来并完成工作

$ eval $(./scripts/env_script.py)
$ echo $FOO
whatever

我想通过进一步调整,可以使env_script.py末尾的页脚更加健壮,然后移至脚本之外。可以定义某种别名以使其在语法上更好,因此可以像pysource ./scripts/env_script.py那样调用它。不过,我对这个答案还是不满意。

编辑:我进行了调整,并编写了可下载的脚本here