从Python中获取Bash脚本的好方法是什么?

从Python中获取Bash脚本的好方法是什么?,python,bash,dictionary,env,Python,Bash,Dictionary,Env,我有一个基本的采购职能: def source( fileName = None, update = True ): pipe = subprocess.Popen(". {fileName}; env".format( fileName = fileName ), stdout = subprocess.PIPE, shell = True) data = pipe.communicate()[0] env = dic

我有一个基本的采购职能:

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
闭合链支架位于第51行


如何从Python中以健壮、合理的方式获取Bash脚本,从而避免此类错误(以及您可能想到的任何其他错误)?

您看到的这行代码是脚本执行以下操作的结果:

module() { eval `/usr/bin/modulecmd bash $*`; }
export -f module
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("\0".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('\0'))
也就是说,它显式地导出
bash
函数
模块
,以便sub(bash)shell可以使用它

我们可以从环境变量的格式告诉我们,在SHILLASH补丁的中间升级了BASH。我不认为当前有一个补丁会生成

BASH\u FUNC\u module()=
而不是
BASH\u FUNC\u module%%()=
,但iirc在一系列修复过程中发布了这样一个补丁。现在事情已经解决了,您可能想再次升级bash。(如果这是剪切粘贴错误,请忽略此段落。)

我们还可以看出系统上的
/bin/sh
bash
,假设
模块
函数是通过寻找shell脚本引入的

也许您应该决定是否关心导出的bash函数。您是想将
模块
导出到您正在创建的环境中,还是忽略它?下面的解决方案只返回它在环境中找到的内容,因此它将包括
模块

简言之,如果要分析某个试图打印环境的shell命令的输出,可能会遇到三个问题:

  • 导出的函数(仅限bash),它们在shellshock补丁之前和之后看起来不同,但始终至少包含一个换行符。(它们的值总是以
    (){
    开头,因此它们很容易识别。在shellshock之后,它们的名称将是
    BASH_FUNC_funcname%%
    ,但除非您在野外找不到经过预打补丁和后打补丁的BASH,否则您可能不想依赖它。)

  • 导出包含换行符的变量

  • 在某些情况下,导出的变量完全没有值。这些变量的值实际上是空字符串,但它们可能在环境列表中没有
    =
    符号,并且一些实用程序将在没有
    =
    符号的情况下打印出来

  • 和往常一样,最健壮(甚至可能最简单)的解决方案是避免解析,但我们可以依靠解析我们自己创建的格式化字符串的策略,这是为解析而精心设计的

    我们可以使用任何能够访问环境的编程语言来生成此输出;为简单起见,我们可以使用python本身。我们将以非常简单的格式输出环境变量:变量名(必须是字母数字),后跟等号,后跟值,后跟NUL(0)字节(无法显示在值中)。类似于以下内容:

    module() { eval `/usr/bin/modulecmd bash $*`; }
    export -f module
    
    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("\0".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('\0'))
    

    我认为通常最好直接使用bash来设置环境,然后在已经设置好的环境中调用python脚本

    如果我正确理解了您的情况,那么您可以使用一些bash脚本来设置希望在python脚本中设置的环境。这些python脚本然后使用准备好的环境来为更多的工具设置更多的环境

    我建议进行以下设置:

  • 酒会包装纸

    • 使用bash脚本设置环境
    • 调用python设置脚本(python脚本从bash脚本继承环境)
  • 您当前的python脚本不包含子流程和环境读取

    • 在上面的bash脚本准备的环境中启动
    • 继续为下一个工具准备环境
  • 通过这种方式,您可以在其“本机环境”中使用每个脚本


    另一种方法是手动将bash脚本转换为python。

    为什么要用python编写shell脚本?您想做什么?将shell变量转换为python变量?遇到这些行时,您希望发生什么?您遇到的是bash shell脚本代码,而不仅仅是环境变量b实际上,我正在尝试运行一个Python脚本,该脚本设置了一个特定的环境,然后生成Python模块(实际上绑定到一个更大的基础设施)可用于导入。我正在尝试在运行源程序的Python环境中提供通过获取shell脚本创建的环境。我应该以什么方式进行此操作?我目前使用的基本方法根本不够可靠。但不是“Python模块”只是一个包含python文件的文件夹?(至少有一个文件名为
    \uuu init\uuuuuuy.py
    )。我会尝试回答你的问题,但你所说的目标对我来说没有意义。我不知道python脚本如何创建python模块。除非你也在脚本的文件系统中动态创建文件,(如果我错了,有人可以纠正我),您无法通过创建子流程来更改python进程的环境变量。在子流程生成bash脚本时,只有子流程的环境变量发生了更改。因此,退出后,脚本的进程将不会发生任何更改。在一般情况下,这可能是可能的,也可能是不可能的。在我的情况下,我需要从python中加载模块,depending在一些编程定义的情况下,因此您的方法不适用于该情况