【问题标题】:How do I tell POpen to use/set certain environment variables?如何告诉 POpen 使用/设置某些环境变量?
【发布时间】:2019-11-19 11:25:33
【问题描述】:

我正在使用 Python 3.7 和 Django。我使用下面的命令来运行我通常会在 shell 中运行的命令...

out = Popen([settings.SELENIUM_RUNNER_CMD, file_path], stderr=STDOUT, stdout=PIPE)
t = out.communicate()[0], out.returncode

他死于错误

b'env: node: No such file or directory\n'

我想要弄清楚的是如何让我的 Python 环境访问我可以访问的正常环境变量,或者在我运行我的 Python 命令之前找出一种设置它们的方法。通常当我以自己的身份检查时很容易找到“节点”

davea$ which node
/usr/local/bin/node

但我不知道如何告诉 Python 使用我可以访问的相同 PATH。

【问题讨论】:

  • 这是你要找的*.com/a/4453495/6163736吗?
  • 代码是如何运行的?它是在uwsgi 服务器下运行还是在其他什么地方运行?
  • settings.SELENIUM_RUNNER_CMDfile_path 的值是多少?
  • @kmaork, SELENIUM_RUNNER_CMD 是“/usr/local/bin/selenium-side-runner”,file_path 是“.side”文件的绝对路径(例如 /tmp/myrecording.side)
  • 你的python程序中os.getenv('PATH')是否包含/usr/local/bin

标签: python django python-3.x environment-variables popen


【解决方案1】:

如果我们引用Popen's documentation,我们可以看到三个相关参数:

  1. cwd strpath 类对象,即当前工作目录
  2. env 映射(比如说dict),这是传递给被调用程序的环境映射
  3. shell 标志,是否将程序包装在外壳中

让我们回顾一下每个解决方案。


如果你负担得起,就使用cwd="where is node",例如,如果node/usr/local/bin中,你可以使用cwd=/usr/local/bincwd=os.path.join(USR_LOCAL, 'bin')。但是,所有内容都将在此文件夹中创建,这可能不是您想要的(日志、当前工作目录的假设)。


现在,对于环境:

如果env不是None,它必须是一个定义新进程的环境变量的映射;这些用于代替继承当前进程环境的默认行为。它直接传递给 Popen。

您可以通过os.environ 复制您当前的环境并在PATH 中添加一些内容,如下所示:

new_env = os.environ.copy()
new_env['PATH'] = '{}:/usr/local/bin'.format(new_env['PATH'])

然后通过这个new_env 映射,就可以了!


如果你真的想依赖 shell,你可以,但这里是平台详细信息:

POSIX 平台

在 shell=True 的 POSIX 上,shell 默认为 /bin/sh。如果 args 是字符串,则该字符串指定要通过 shell 执行的命令。这意味着字符串的格式必须与在 shell 提示符下键入时的格式完全相同。这包括,例如,引用或反斜杠转义文件名,其中包含空格。如果 args 是一个序列,则第一项指定命令字符串,任何附加项将被视为 shell 本身的附加参数。也就是说,Popen 相当于:Popen(['/bin/sh', '-c', args[0], args[1], ...])

Windows 平台

在 shell=True 的 Windows 上,COMSPEC 环境变量指定默认 shell。您需要在 Windows 上指定 shell=True 的唯一时间是您希望执行的命令内置于 shell 中(例如 dir 或 copy)。您不需要 shell=True 来运行批处理文件或基于控制台的可执行文件。

您可以使用 PATH=whatever 之类的东西并直接使用整个 shell-fu,但需要注意的是:安全考虑


奖励解决方案

只需在调用 Python 进程之前重新定义 PATH。如果您使用的是 Django,那么您正在使用:

  1. 开发服务器
  2. 生产级服务器

在这两种情况下,您所要做的就是重新定义父进程的环境,对于像 Gunicorn 这样的生产级服务器,这是可能的,并且有文档可以做到这一点。对于开发服务器,请在您自己的 shell 级别执行此操作(但警告!您可能必须记录此类行为或告诉使用您的软件的任何人您假设 node 处于……大多数时候公平的路径) .

【讨论】:

    【解决方案2】:

    os.environ.copy() 最适合您的需求。

    import subprocess, os
    my_env = os.environ.copy()
    out = Popen([settings.SELENIUM_RUNNER_CMD, file_path], stderr=STDOUT, stdout=PIPE, env=my_env)
    t = out.communicate()[0], out.returncode
    

    应该就是这样!

    【讨论】: