Python、bash/python/subprocess进程(shell)之间的关系?

Python, the relationship between the bash/python/subprocess processes (shells)?

当尝试用 python 编写脚本时,我有一个基本的知识漏洞。

更新: 多亏了答案,我将 shell 一词更正为 process/subprocess

命名法

测试 process/subprocess 是否相等

SUBPROCESS_A 开始 SUBPROCESS_B。在下面的问题中,当我说 SUBPROCESS_A == SUBPROCESS_B 时,我的意思是如果 SUBPROCESS_B 设置了一个环境变量,当它 运行 完成时,他们会在 SUBPROCESS_A 中设置 env 变量?如果 SUBPROCESS_B 中有一个 运行s eval "$(ssh-agent -s)"SUBPROCESS_A 现在也会有一个 ssh 代理吗?

问题

使用上述命名法和相等性测试

  1. BASH_PROCESS==PYTHON_SUBPROCESS吗?
  2. PYTHON_SUBPROCESS == SUBPROCESS_SUBPROCESS吗?
  3. PYTHON_SUBPROCESS == SUBPROCESS_SUBPROCESS=True吗?
  4. 如果 SUBPROCESS_SUBPROCESS=True 不等于 BASH_PROCESS,那么如何改变执行环境(例如 eval "$(ssh-agent -s)")以便 python 脚本可以设置来电者的环境?

None 这些等式是正确的,其中一半“shell”实际上不是 shell。

你的 bash shell 是 shell。当您从 shell 启动 Python 脚本时,运行 脚本的 Python 进程是 bash shell 的子进程过程。当您从 Python 脚本启动子进程时,该子进程是 Python 进程的子进程。如果您使用 shell=True 启动子进程,Python 调用 shell 来解析和 运行 命令,但除此之外,[=35] 不涉及 shell =]宁子进程。

子进程在启动时从其父进程继承环境变量(除非您采取特定步骤来避免这种情况),但它们不能为其父进程设置环境变量。您不能 运行 Python 脚本来设置 shell 中的环境变量,或者 运行 来自 Python 的子进程来设置 Python 脚本的环境变量。

您似乎混淆了这里的几个概念。

TLDR 不,子进程无法更改其父进程的环境。另见 Global environment variables in a shell script

你好像真的不是在问“shells”。 相反,这些是 子流程; 如果您 运行 python foo.py 在 shell 中,Python 流程是 [=] 的子流程125=] 过程。 (许多 shell 允许您 exec python foo.py 将 shell 进程替换为 Python 进程;此进程现在是启动 shell 的进程的子进程。在 Unix-like 系统上,最终所有进程都是进程 1 的后代,即 init 进程。)

subprocess 运行 简单来说就是一个子进程。如果 shell=True 则 Python 的直接子进程是 shell,而您 运行 的命令是 shell 的子进程。 shell 将是默认值 shell(Windows 上的 cmd,Unix-like 系统上的 /bin/sh),尽管您可以使用例如显式覆盖它executable="/bin/bash"

示例:

  • subprocess.Popen(['printf', '%s\n', 'foo', 'bar'])

    Python是父进程,printf是父进程是Python的子进程。

  • subprocess.Popen(r"printf '%s\n' foo bar", shell=True)

    Python是/bin/sh的父进程,而/bin/sh又是printf的父进程。当 printf 终止时,sh 也会终止,因为它已到达其脚本的末尾。

    也许注意到 shell 负责解析命令行并将其拆分为四个标记,我们最终在上一个示例中显式地直接传递给 Popen

    您 运行 的命令可以访问 shell 通配符扩展、管道、重定向、引用、变量扩展、后台处理等功能

    在这个孤立的示例中,使用了其中的 none 个,因此您基本上是在添加一个不必要的进程。 (如果您想避免将命令拆分为标记的轻微负担,可以使用 shlex.split()。)另请参阅 Actual meaning of 'shell=True' in subprocess

  • subprocess.Popen(r"printf '%s\n' foo bar", shell=True, executable="/bin/bash")

    Python是Bash的父进程,而Bash又是printf的父进程。除了 shell 的名称外,这与前面的示例相同。

    在某些情况下,当您要执行的命令需要 Bash 中可用的功能时,您确实需要更慢且更多的 memory-hungry Bash shell ,但不在谍影重重 shell。一般来说,更好的解决方案几乎总是 运行 在子进程中尽可能少的代码,而不是用原生 Python 构造替换那些 Bash 命令;但如果您知道自己在做什么(或者 真的 不知道自己在做什么,但需要完成工作而不是正确解决问题),该工具可能会有用.

(另外,您应该尽可能避免裸露 Popen,如 subprocess 文档中所述。)

子进程在启动时会继承其父进程的环境。在 Unix-like 系统上,进程无法更改其父进程的环境(尽管父进程可能参与使之成为可能,如您的 eval 示例)。

为了完成您最终可能要问的问题,您可以在 Python 中设置一个环境,然后将您的其他命令作为子进程启动,也许然后使用显式 env= 关键字参数指向您希望它使用的环境:

import os

...
env = os.environ.copy()
env["PATH"] = "/opt/foo:" + env["PATH"]
del env["PAGER"]
env["secret_cookie"] = "xyzzy"
subprocess.Popen(["otherprogram"], env=env)

或让 Python 打印出可以安全地传递给 Bourne shell 中的 eval 的表单中的值。 (注意:这需要您了解 eval 的一般风险,特别是目标 shell 的引用约定;此外,您可能需要支持多个 shell,除非您只针对非常有限的受众。)

... 虽然在许多情况下,到目前为止最简单的解决方案是在 shell 中设置环境,然后 运行 Python 作为 shell 实例(或 exec python,如果你想在 shell 实例完成它的部分后摆脱它;另见 What are the uses of the exec command in shell scripts?


Python 不带参数启动 Python REPL,它可以被视为“shell”,尽管我们通常不会使用该术语(也许改为称它为“交互式口译员”——另见下文);但是 python foo.py 只是 运行 脚本 foo.py 并退出,所以那里没有 shell。

“shell”的定义有点context-dependent,但你这里好像真的不是在问shell。 (一些 GUI 有“图形 shell” 等概念,但我们已经超出了您要询问的范围。)一些程序是命令解释器(Python 可执行文件解释并执行Python 语言中的命令;Bourne shell 解释和执行 shell 脚本)但通常只有那些主要目的包括 运行 其他程序的程序才被称为“shells".