从 Python 中获取 Bash 脚本的好方法是什么?
What is a good way to source a Bash script from within Python?
我有基本的采购功能:
def source(
fileName = None,
update = True
):
pipe = subprocess.Popen(". {fileName}; env".format(
fileName = fileName
), stdout = subprocess.PIPE, shell = True)
data = pipe.communicate()[0]
env = dict((line.split("=", 1) for line in data.splitlines()))
if update is True:
os.environ.update(env)
return(env)
当我尝试使用它来获取特定脚本时,出现以下错误:
>>> source("/afs/cern.ch/sw/lcg/contrib/gcc/4.8/x86_64-slc6/setup.sh")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 9, in source
ValueError: dictionary update sequence element #51 has length 1; 2 is required
这是由可执行文件 env
返回的以下行引起的:
BASH_FUNC_module()=() { eval `/usr/bin/modulecmd bash $*`
}
结束链括号在第 51 行。
应该如何以可靠、明智的方式从 Python 中获取 Bash 脚本,从而避免此类错误(以及您能想到的任何其他可能的错误)?
您看到的行是脚本执行以下操作的结果:
module() { eval `/usr/bin/modulecmd bash $*`; }
export -f module
即显式导出bash
函数module
以便sub(bash)shells可以使用它
我们可以从环境变量的格式得知您在 shell 休克补丁中升级了 bash。我不认为当前有一个补丁会生成 BASH_FUNC_module()=
而不是 BASH_FUNC_module%%()=
,但是 iirc 在一系列修复期间分发了这样一个补丁。既然事情已经解决,您可能想再次升级 bash。 (如果那是剪切和粘贴错误,请忽略这一段。)
而且我们还可以知道您系统上的 /bin/sh
是 bash
,假设 module
函数是通过采购 shell 脚本引入的。
可能您应该决定是否关心导出的 bash 函数。您要将 module
导出到您正在创建的环境中,还是忽略它?下面的解决方案只是 returns 它在环境中找到的内容,因此它将包括 module
.
简而言之,如果您要解析某些尝试打印环境的 shell 命令的输出,您将遇到三个可能的问题:
导出的函数(仅bash),在 post-shellshock 补丁之前看起来不同,但始终包含至少一个换行符。 (它们的值总是以 () {
开头,所以它们很容易识别。Post shellshock,它们的名字将是 BASH_FUNC_funcname%%
但直到你找不到 pre- 和post-补丁 bashes 在野外,你可能不想依赖它。)
导出包含换行符的变量。
在某些情况下,导出的变量根本没有值。这些实际上具有空字符串的值,但它们可能在没有 =
符号的环境列表中,并且一些实用程序将在没有 =
.[=25= 的情况下打印它们]
一如既往,最稳健(甚至可能最简单)的解决方案是避免解析,但我们可以退回到解析我们自己创建的格式化字符串的策略,该字符串经过精心设计以供解析。
我们可以使用任何可以访问环境的编程语言来生成此输出;为简单起见,我们可以使用 python 本身。我们将以非常简单的格式输出环境变量:变量名(必须是字母数字),后跟等号,后跟值,后跟 NUL (0) 字节(不能出现在值中) .类似于以下内容:
from subprocess import Popen, PIPE
# The commented-out line really should not be necessary; it's impossible
# for an environment variable name to contain an =. However, it could
# be replaced with a more stringent check.
prog = ( r'''from os import environ;'''
+ r'''from sys import stdout;'''
+ r'''stdout.write("[=11=]".join("{k}={v}".format(kv)'''
+ r''' for kv in environ.iteritems()'''
#+ r''' if "=" not in kv[0]'''
+ r''' ))'''
)
# Lots of error checking omitted.
def getenv_after_sourcing(fn):
argv = [ "bash"
, "-c"
, '''. "{fn}"; python -c '{prog}' '''.format(fn=fn, prog=prog)]
data = Popen(argv, stdout=PIPE).communicate()[0]
return dict(kv.split('=', 1) for kv in data.split('[=11=]'))
我觉得一般直接使用bash设置环境,然后在已经设置好的环境中调用python脚本比较好。这是利用核心 unix/linux 原则之一:子进程继承父进程环境的副本。
如果我正确理解了您的情况,那么您有一些 bash 脚本可以设置一些您希望在 python 脚本中拥有的环境。这些 python 脚本然后使用准备好的环境为更多工具设置更多环境。
我建议以下设置:
一个bash包装器
- 使用 bash 脚本设置环境
- 调用您的 python 安装脚本(python 脚本从 bash 脚本继承环境)
您当前的 python 脚本没有子进程和环境读取
- 在上面的bash脚本准备的环境中启动
- 继续为下一个工具准备环境
这样您就可以在 "native environment".
中使用每个脚本
另一种方法是手动将 bash 脚本翻译成 python。
我有基本的采购功能:
def source(
fileName = None,
update = True
):
pipe = subprocess.Popen(". {fileName}; env".format(
fileName = fileName
), stdout = subprocess.PIPE, shell = True)
data = pipe.communicate()[0]
env = dict((line.split("=", 1) for line in data.splitlines()))
if update is True:
os.environ.update(env)
return(env)
当我尝试使用它来获取特定脚本时,出现以下错误:
>>> source("/afs/cern.ch/sw/lcg/contrib/gcc/4.8/x86_64-slc6/setup.sh")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 9, in source
ValueError: dictionary update sequence element #51 has length 1; 2 is required
这是由可执行文件 env
返回的以下行引起的:
BASH_FUNC_module()=() { eval `/usr/bin/modulecmd bash $*`
}
结束链括号在第 51 行。
应该如何以可靠、明智的方式从 Python 中获取 Bash 脚本,从而避免此类错误(以及您能想到的任何其他可能的错误)?
您看到的行是脚本执行以下操作的结果:
module() { eval `/usr/bin/modulecmd bash $*`; }
export -f module
即显式导出bash
函数module
以便sub(bash)shells可以使用它
我们可以从环境变量的格式得知您在 shell 休克补丁中升级了 bash。我不认为当前有一个补丁会生成 BASH_FUNC_module()=
而不是 BASH_FUNC_module%%()=
,但是 iirc 在一系列修复期间分发了这样一个补丁。既然事情已经解决,您可能想再次升级 bash。 (如果那是剪切和粘贴错误,请忽略这一段。)
而且我们还可以知道您系统上的 /bin/sh
是 bash
,假设 module
函数是通过采购 shell 脚本引入的。
可能您应该决定是否关心导出的 bash 函数。您要将 module
导出到您正在创建的环境中,还是忽略它?下面的解决方案只是 returns 它在环境中找到的内容,因此它将包括 module
.
简而言之,如果您要解析某些尝试打印环境的 shell 命令的输出,您将遇到三个可能的问题:
导出的函数(仅bash),在 post-shellshock 补丁之前看起来不同,但始终包含至少一个换行符。 (它们的值总是以
() {
开头,所以它们很容易识别。Post shellshock,它们的名字将是BASH_FUNC_funcname%%
但直到你找不到 pre- 和post-补丁 bashes 在野外,你可能不想依赖它。)导出包含换行符的变量。
在某些情况下,导出的变量根本没有值。这些实际上具有空字符串的值,但它们可能在没有
=
符号的环境列表中,并且一些实用程序将在没有=
.[=25= 的情况下打印它们]
一如既往,最稳健(甚至可能最简单)的解决方案是避免解析,但我们可以退回到解析我们自己创建的格式化字符串的策略,该字符串经过精心设计以供解析。
我们可以使用任何可以访问环境的编程语言来生成此输出;为简单起见,我们可以使用 python 本身。我们将以非常简单的格式输出环境变量:变量名(必须是字母数字),后跟等号,后跟值,后跟 NUL (0) 字节(不能出现在值中) .类似于以下内容:
from subprocess import Popen, PIPE
# The commented-out line really should not be necessary; it's impossible
# for an environment variable name to contain an =. However, it could
# be replaced with a more stringent check.
prog = ( r'''from os import environ;'''
+ r'''from sys import stdout;'''
+ r'''stdout.write("[=11=]".join("{k}={v}".format(kv)'''
+ r''' for kv in environ.iteritems()'''
#+ r''' if "=" not in kv[0]'''
+ r''' ))'''
)
# Lots of error checking omitted.
def getenv_after_sourcing(fn):
argv = [ "bash"
, "-c"
, '''. "{fn}"; python -c '{prog}' '''.format(fn=fn, prog=prog)]
data = Popen(argv, stdout=PIPE).communicate()[0]
return dict(kv.split('=', 1) for kv in data.split('[=11=]'))
我觉得一般直接使用bash设置环境,然后在已经设置好的环境中调用python脚本比较好。这是利用核心 unix/linux 原则之一:子进程继承父进程环境的副本。
如果我正确理解了您的情况,那么您有一些 bash 脚本可以设置一些您希望在 python 脚本中拥有的环境。这些 python 脚本然后使用准备好的环境为更多工具设置更多环境。
我建议以下设置:
一个bash包装器
- 使用 bash 脚本设置环境
- 调用您的 python 安装脚本(python 脚本从 bash 脚本继承环境)
您当前的 python 脚本没有子进程和环境读取
- 在上面的bash脚本准备的环境中启动
- 继续为下一个工具准备环境
这样您就可以在 "native environment".
中使用每个脚本另一种方法是手动将 bash 脚本翻译成 python。