从subprocess.Popen调用"source"命令

cof*_*fee 32 python unix popen

我有一个.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
Run Code Online (Sandbox Code Playgroud)

是什么赋予了?为什么我不能从Popen中调用"source",当我可以在python之外?

Sin*_*ion 30

source 它不是一个可执行命令,它是一个内置的shell.

最常用的情况source是运行一个shell脚本来更改环境并在当前shell中保留该环境.这正是virtualenv如何修改默认的python环境.

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

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

  • 注意:Python3 中 `execfile()` 被替换为 `exec()` (2认同)

xAp*_*ple 28

您可以在子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)
Run Code Online (Sandbox Code Playgroud)

  • 如果环境变量的值包含换行符,则此函数可能引发"ValueError".要[修复](http://stackoverflow.com/a/20669683/190597),请使用`env -0`和`output.split('\ x00')`. (3认同)
  • 应该归功于它:这来自 http://pythonwise.blogspot.fr/2010/04/sourcing-shell-script.html(虽然可能是 xApple == Miki?)但有一个说明:一般来说,脚本参数需要是一个明确的路径,即“myenv.sh”一般不会工作,但“./myenv.sh”会。这是因为在严格实现 sh shell 的系统(例如 Debian/Ubuntu)上的采购内置 (.) 行为。 (2认同)

jfs*_*jfs 14

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

破坏Popen("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")
Run Code Online (Sandbox Code Playgroud)

正如@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 '\0'
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('\0'))
pprint(dict(os.environ)) #NOTE: only `export`ed envvars here
Run Code Online (Sandbox Code Playgroud)

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

为了支持任意字节os.environb,json可以使用模块(假设我们使用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))
Run Code Online (Sandbox Code Playgroud)


phi*_*hag 6

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

foo = subprocess.Popen(["/bin/sh", "the_script.sh"])
Run Code Online (Sandbox Code Playgroud)