从 subprocess.Popen 调用“source”命令

2025-02-20 09:23:00
admin
原创
31
摘要:问题描述:我有一个用 调用的 .sh 脚本source the_script.sh。定期调用它是可以的。但是,我尝试通过 在我的 Python 脚本中调用它subprocess.Popen。从 Popen 调用它,我在以下两种场景调用中收到以下错误:foo = subprocess.Popen("s...

问题描述:

我有一个用 调用的 .sh 脚本source the_script.sh。定期调用它是可以的。但是,我尝试通过 在我的 Python 脚本中调用它subprocess.Popen

从 Popen 调用它,我在以下两种场景调用中收到以下错误:

foo = subprocess.Popen("source the_script.sh")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/subprocess.py", line 672, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1213, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory


>>> foo = subprocess.Popen("source the_script.sh", shell = True)
>>> /bin/sh: source: not found

怎么回事?为什么我不能从 Popen 调用“source”,而可以在 Python 之外调用?


解决方案 1:

source不是可执行命令,而是 shell 内置命令。

最常见的使用情况source是运行更改环境的 shell 脚本,并在当前 shell 中保留该环境。这正是 virtualenv 修改默认 python 环境的工作原理。

创建子进程并source在子进程中使用可能不会做任何有用的事情,它不会修改父进程的环境,也不会发生使用源脚本的任何副作用。

Python 有一个类似的命令,execfile它使用当前 python 全局命名空间(或另一个,如果您提供的话)运行指定的文件,您可以以类似于 bash 命令的方式使用它source

解决方案 2:

您可以在子 shell 中运行该命令并使用结果来更新当前环境。

def shell_source(script):
    """Sometime you want to emulate the action of "source" in bash,
    settings some environment variables. Here is a way to do it."""
    import subprocess, os
    pipe = subprocess.Popen(". %s; env" % script, stdout=subprocess.PIPE, shell=True)
    output = pipe.communicate()[0]
    env = dict((line.split("=", 1) for line in output.splitlines()))
    os.environ.update(env)

解决方案 3:

BrokenPopen("source the_script.sh")相当于Popen(["source the_script.sh"])尝试启动'source the_script.sh'程序失败。找不到它,因此"No such file or directory"出错。

BrokenPopen("source the_script.sh", shell=True)失败,因为source是 bash 内置命令(help source在 bash 中输入),但默认 shell/bin/sh无法理解它(/bin/sh使用.)。假设中可能还有其他 bash-ism the_script.sh,则应使用 bash 运行它:

foo = Popen("source the_script.sh", shell=True, executable="/bin/bash")

正如@IfLoop 所说,在子进程中执行不是很有用,source因为它不会影响父进程的环境。

os.environ.update(env)如果the_script.sh执行unset某些变量,基于的方法将会失败。os.environ.clear()可以调用它来重置环境:

#!/usr/bin/env python2
import os
from pprint import pprint
from subprocess import check_output

os.environ['a'] = 'a'*100
# POSIX: name shall not contain '=', value doesn't contain ''
output = check_output("source the_script.sh; env -0",   shell=True,
                      executable="/bin/bash")
# replace env
os.environ.clear() 
os.environ.update(line.partition('=')[::2] for line in output.split(''))
pprint(dict(os.environ)) #NOTE: only `export`ed envvars here

它使用env -0并由.split('')@unutbu 建议

为了支持中的任意字节os.environbjson可以使用模块(假设我们使用的 Python 版本已修复“json.dumps 无法被 json.loads 解析”问题):

为了避免通过管道传递环境,可以更改 Python 代码以在子进程环境中调用自身,例如:

#!/usr/bin/env python2
import os
import sys
from pipes import quote
from pprint import pprint

if "--child" in sys.argv: # executed in the child environment
    pprint(dict(os.environ))
else:
    python, script = quote(sys.executable), quote(sys.argv[0])
    os.execl("/bin/bash", "/bin/bash", "-c",
        "source the_script.sh; %s %s --child" % (python, script))

解决方案 4:

source是内置的 bash 专用 shell(非交互式 shell 通常是轻量级 dash shell,而不是 bash)。相反,只需调用/bin/sh

foo = subprocess.Popen(["/bin/sh", "the_script.sh"])

解决方案 5:

更新:2019 年

"""
    Sometime you want to emulate the action of "source" in bash,
    settings some environment variables. Here is a way to do it.
"""
def shell_source( str_script, lst_filter ):
    #work around to allow variables with new lines
    #example MY_VAR='foo
'
    #env -i create clean shell
    #bash -c run bash command
    #set -a optional include if you want to export both shell and enrivonment variables
    #env -0 seperates variables with null char instead of newline
    command = shlex.split(f"env -i bash -c 'set -a && source {str_script} && env -0'")

    pipe = subprocess.Popen( command, stdout=subprocess.PIPE )
    #pipe now outputs as byte, so turn it to utf string before parsing
    output = pipe.communicate()[0].decode('utf-8')
    #There was always a trailing empty line for me so im removing it. Delete this line if this is not happening for you.
    output = output[:-1]

    pre_filter_env = {}
    #split using null char
    for line in output.split('x00'):
        line = line.split( '=', 1)
        pre_filter_env[ line[0]] = line[1]

    post_filter_env = {}
    for str_var in lst_filter:
        post_filter_env[ str_var ] = pre_filter_env[ str_var ]

    os.environ.update( post_filter_env )

解决方案 6:

@xApple 的答案的一个变体,因为有时能够获取 shell 脚本(而不是 Python 文件)来设置环境变量并可能执行其他 shell 操作,然后将该环境传播到 Python 解释器而不是在子 shell 关闭时丢失该信息很有用。

之所以要进行修改,是因为“env”输出的每行一个变量的假设并不是 100% 稳健的:我只需要处理一个包含换行符的变量(我认为是一个 shell 函数),这会搞砸解析。因此,这里有一个稍微复杂一点的版本,它使用 Python 本身以稳健的方式格式化环境字典:

import subprocess
pipe = subprocess.Popen(". ./shellscript.sh; python -c 'import os; print \"newenv = %r\" % os.environ'", 
    stdout=subprocess.PIPE, shell=True)
exec(pipe.communicate()[0])
os.environ.update(newenv)

也许有更简洁的方法?这还可以确保如果有人将 echo 语句放入正在获取的脚本中,环境解析不会混乱。当然,这里有一个 exec,所以要小心不受信任的输入……但我认为这在关于如何获取/执行任意 shell 脚本的讨论中是隐含的 ;-)

更新:请参阅@unutbu 对@xApple 答案的评论,了解处理输出中的换行符的另一种(可能更好)方法env

解决方案 7:

利用这里的答案,我创建了一个符合我需要的解决方案。

  • 不需要过滤环境变量

  • 允许使用换行符的变量

def shell_source(script):
    """
    Sometime you want to emulate the action of "source" in bash,
    settings some environment variables. Here is a way to do it.
    """
    
    pipe = subprocess.Popen(". %s && env -0" % script, stdout=subprocess.PIPE, shell=True)
    output = pipe.communicate()[0].decode('utf-8')
    output = output[:-1] # fix for index out for range in 'env[ line[0] ] = line[1]'

    env = {}
    # split using null char
    for line in output.split('x00'):
        line = line.split( '=', 1)
        # print(line)
        env[ line[0] ] = line[1]

    os.environ.update(env)

这样,我就可以毫无问题地运行具有相同环境变量的命令:

def runCommand(command):
    """
    Prints and then runs shell command.
    """
    print(f'> running: {command}')
    stream = subprocess.Popen(command, shell=True,env=os.environ)
    (result_data, result_error) = stream.communicate()
    print(f'{result_data}, {result_error}')

希望这能帮助和我处境相同的人

解决方案 8:

如果您想将 source 命令应用于其他脚本或可执行文件,那么您可以创建另一个包装脚本文件,并使用您需要的任何其他逻辑从中调用“source”命令。在这种情况下,此 source 命令将修改其运行的本地上下文 - 即 subprocess.Popen 创建的子进程中。

如果您需要修改程序正在运行的 python 上下文,那么这将不起作用。

解决方案 9:

这个问题似乎有很多答案,我还没有全部读完,所以他们可能已经指出了这一点;但是,当调用这样的 shell 命令时,你必须将 shell=True 传递给 Popen 调用。否则,你可以调用 Popen(shlex.split())。确保导入 shlex。

我实际上使用此功能是为了获取文件并修改当前环境。

def set_env(env_file):
    while True:
        source_file = '/tmp/regr.source.%d'%random.randint(0, (2**32)-1)
        if not os.path.isfile(source_file): break
    with open(source_file, 'w') as src_file:
        src_file.write('#!/bin/bash
')
        src_file.write('source %s
'%env_file)
        src_file.write('env
')
    os.chmod(source_file, 0755)
    p = subprocess.Popen(source_file, shell=True,
                         stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    (out, err) = p.communicate()
    setting = re.compile('^(?P<setting>[^=]*)=')
    value = re.compile('=(?P<value>.*$)')
    env_dict = {}
    for line in out.splitlines():
        if setting.search(line) and value.search(line):
            env_dict[setting.search(line).group('setting')] = value.search(line).group('value')
    for k, v in env_dict.items():
        os.environ[k] = v
    for k, v in env_dict.items():
        try:
            assert(os.getenv(k) == v)
        except AssertionError:
            raise Exception('Unable to modify environment')
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1325  
  IPD(Integrated Product Development)流程作为一种先进的产品开发管理模式,在众多企业中得到了广泛应用。它涵盖了从产品概念产生到产品退市的整个生命周期,通过整合跨部门团队、优化流程等方式,显著提升产品开发的效率和质量,进而为项目的成功奠定坚实基础。深入探究IPD流程的五个阶段与项目成功之间...
IPD流程分为几个阶段   4  
  华为作为全球知名的科技企业,其成功背后的管理体系备受关注。IPD(集成产品开发)流程作为华为核心的产品开发管理模式,其中的创新管理与实践更是蕴含着丰富的经验和深刻的智慧,对众多企业具有重要的借鉴意义。IPD流程的核心架构IPD流程旨在打破部门墙,实现跨部门的高效协作,将产品开发视为一个整体的流程。它涵盖了从市场需求分析...
华为IPD是什么   3  
  IPD(Integrated Product Development)研发管理体系作为一种先进的产品开发模式,在众多企业的发展历程中发挥了至关重要的作用。它不仅仅是一套流程,更是一种理念,一种能够全方位提升企业竞争力,推动企业持续发展的有效工具。深入探究IPD研发管理体系如何助力企业持续发展,对于众多渴望在市场中立足并...
IPD管理流程   3  
  IPD(Integrated Product Development)流程管理旨在通过整合产品开发流程、团队和资源,实现产品的快速、高质量交付。在这一过程中,有效降低成本是企业提升竞争力的关键。通过优化IPD流程管理中的各个环节,可以在不牺牲产品质量和性能的前提下,实现成本的显著降低,为企业创造更大的价值。优化产品规划...
IPD流程分为几个阶段   4  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用