Python,bash/python/子进程进程(shell)之间的关系?

发布于 2025-01-13 17:29:18 字数 1368 浏览 6 评论 0原文

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

更新:感谢您的回答,我将 shell 一词更正为 process/subprocess

命名法

  • 从 Bash 提示符开始,让我们称之为 BASH_PROCESS
  • 然后在 BASH_PROCESS 中我运行 python3 foo.py,python 脚本在 PYTHON_SUBPROCESS
  • 运行foo.py 是对 subprocess.run(...) 的调用,这个子进程命令运行在 `SUBPROCESS_SUBPROCESS 中,
  • foo.py 中是subprocess.run(..., shell=True),此子进程命令运行于 SUBPROCESS_SUBPROCESS=True

测试进程/子进程是否相等

Say SUBPROCESS_A 启动 SUBPROCESS_B。在下面的问题中,当我说 is SUBPROCESS_A == SUBPROCESS_B 时,我的意思是如果 SUBPROCESS_B 设置了一个 env 变量,当它运行到完成后,它们的环境变量会在 SUBPROCESS_A 中设置吗?如果在 SUBPROCESS_B 中运行 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)" code>) 以便 python 脚本可以为调用者设置环境?

When trying to write script with python, I have a fundamental hole of knowledge.

Update: Thanks to the answers I corrected the word shell to process/subprocess

Nomenclature

  • Starting with a Bash prompt, lets call this BASH_PROCESS
  • Then within BASH_PROCESS I run python3 foo.py, the python script runs in say PYTHON_SUBPROCESS
  • Within foo.py is a call to subprocess.run(...), this subprocess command runs in say `SUBPROCESS_SUBPROCESS
  • Within foo.py is subprocess.run(..., shell=True), this subprocess command runs in say SUBPROCESS_SUBPROCESS=True

Test for if a process/subprocess is equal

Say SUBPROCESS_A starts SUBPROCESS_B. In the below questions, when I say is SUBPROCESS_A == SUBPROCESS_B, what I means is if SUBPROCESS_B sets an env variable, when it runs to completion, will they env variable be set in SUBPROCESS_A? If one runs eval "$(ssh-agent -s)" in SUBPROCESS_B, will SUBPROCESS_A now have an ssh agent too?

Question

Using the above nomenclature and equality tests

  1. Is BASH_PROCESS == PYTHON_SUBPROCESS?
  2. Is PYTHON_SUBPROCESS == SUBPROCESS_SUBPROCESS?
  3. Is PYTHON_SUBPROCESS == SUBPROCESS_SUBPROCESS=True?
  4. If SUBPROCESS_SUBPROCESS=True is not equal to BASH_PROCESS, then how does one alter the executing environment (e.g. eval "$(ssh-agent -s)") so that a python script can set up the env for the calller?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

失退 2025-01-20 17:29:18

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

TLDR 不,子进程无法更改其父进程的环境。另请参阅 shell 脚本中的全局环境变量

你真的不知道似乎在问“贝壳”。
相反,这些是子进程;如果您在 shell 中运行 python foo.py,则 Python 进程是 shell 进程的子进程。 (许多 shell 允许您exec python foo.py,它用 Python 进程替换 shell 进程;该进程现在是启动 shell 的进程的子进程。在类 Unix 系统上,最终所有进程都是进程 1(init 进程)的后代。)

subprocess 简单地运行一个子进程。如果 shell=True 则 Python 的直接子进程是 shell,并且您运行的命令是该 shell 的子进程。 shell 将是默认 shell(Windows 上的 cmd,类 Unix 系统上的 /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 功能,例如通配符扩展、管道、重定向、引用、变量扩展、后台处理等。

    在这个孤立的示例中,没有使用这些,因此您基本上是在添加不必要的进程。 (如果您想避免将命令拆分为令牌的小负担,也许可以使用 shlex.split() 。)另请参阅 子进程中“shell=True”的实际含义

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

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

    在某些情况下,当您要执行的命令需要 Bash 中提供的功能(但 Bourne shell 中不提供)时,您确实需要更慢且更耗内存的 Bash shell。一般来说,更好的解决方案几乎总是在子进程中运行尽可能少的代码,并用本机 Python 结构替换这些 Bash 命令;但如果您知道自己在做什么(或者真的不知道自己在做什么,但需要完成工作而不是正确解决问题),那么该设施可能会很有用。

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

子进程在启动时继承其父进程的环境。在类 Unix 系统上,进程无法更改其父进程的环境(尽管父进程可能参与其中,如您的 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 实例在它完成其部分之后;另请参阅什么exec 命令在 shell 脚本中的用途是什么?


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

“shell”的定义稍微依赖于上下文,但您似乎并没有真正在这里询问 shell。 (有些 GUI 有“图形 shell”等概念,但我们已经超出了您想要询问的范围。)有些程序是命令解释器(Python 可执行文件解释并执行 Python 语言中的命令;Bourne shell 解释并执行 shell 脚本),但通常只有那些主要目的包括运行其他程序的才被称为“shell”。

You seem to be confusing several concepts here.

TLDR No, there is no way for a subprocess to change its parent's environment. See also Global environment variables in a shell script

You really don't seem to be asking about "shells".
Instead, these are subprocesses; if you run python foo.py in a shell, the Python process is a subprocess of the shell process. (Many shells let you exec python foo.py which replaces the shell process with a Python process; this process is now a subprocess of whichever process started the shell. On Unix-like systems, ultimately all processes are descendants of process 1, the init process.)

subprocess runs a subprocess, simply. If shell=True then the immediate subprocess of Python is the shell, and the command(s) you run are subprocesses of that shell. The shell will be the default shell (cmd on Windows, /bin/sh on Unix-like systems) though you can explicitly override this with e.g. executable="/bin/bash"

Examples:

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

    Python is the parent process, printf is a subprocess whose parent is the Python process.

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

    Python is the parent process of /bin/sh, which in turn is the parent process of printf. When printf terminates, so does sh, as it has reached the end of its script.

    Perhaps notice that the shell takes care of parsing the command line and splitting it up into the four tokens we ended up explicitly passing directly to Popen in the previous example.

    The commands you run have access to shell features like wildcard expansion, pipes, redirection, quoting, variable expansion, background processing, etc.

    In this isolated example, none of those are used, so you are basically adding an unnecessary process. (Maybe use shlex.split() if you want to avoid the minor burden of splitting up the command into tokens.) See also Actual meaning of 'shell=True' in subprocess

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

    Python is the parent process of Bash, which in turn is the parent process of printf. Except for the name of the shell, this is identical to the previous example.

    There are situations where you really need the slower and more memory-hungry Bash shell, when the commands you want to execute require features which are available in Bash, but not in the Bourne shell. In general, a better solution is nearly always to run as little code as possible in a subprocess, and instead replace those Bash commands with native Python constructs; but if you know what you are doing (or really don't know what you are doing, but need to get the job done rather than solve the problem properly), the facility can be useful.

(Separately, you should probably avoid bare Popen when you can, as explained in the subprocess documentation.)

Subprocesses inherit the environment of their parent when they are started. On Unix-like systems, there is no way for a process to change its parent's environment (though the parent may participate in making that possible, as in your eval example).

To perhaps accomplish what you may ultimately be asking about, you can set up an environment within Python and then start your other command as a subprocess, perhaps then with an explicit env= keyword argument to point to the environment you want it to use:

import os

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

or have Python print out values in a form which can safely be passed to eval in the Bourne shell. (Caution: this requires you to understand the perils of eval in general and the target shell's quoting conventions in particular; also, you will perhaps need to support the syntax of more than one shell, unless you are only targeting a very limited audience.)

... Though in many situations, the simplest solution by far is to set up the environment in the shell, then run Python as a subprocess of that shell instance (or exec python if you want to get rid of the shell instance after it has performed its part; see also What are the uses of the exec command in shell scripts?)


Python without an argument starts the Python REPL, which could be regarded as a "shell", though we would commonly not use that term (perhaps instead call it "interactive interpreter" - see also below); but python foo.py simply runs the script foo.py and exits, so there is no shell there.

The definition of "shell" is slightly context-dependent, but you don't really seem to be asking about shells here. (Some GUIs have a concept of "graphical shell" etc but we are already out of the scope of what you were trying to ask about.) Some programs are command interpreters (the Python executable interprets and executes commands in the Python language; the Bourne shell interprets and executes shell scripts) but generally only those whose primary purposes include running other programs are called "shells".

飞烟轻若梦 2025-01-20 17:29:18

这些等式都不是真的,而且这些“贝壳”有一半实际上并不是贝壳。

您的 bash shell 是一个 shell。当您从该 shell 启动 Python 脚本时,运行该脚本的 Python 进程是 bash shell 进程的子进程。当您从 Python 脚本启动子进程时,该子进程是 Python 进程的子进程。如果使用 shell=True 启动子进程,Python 会调用 shell 来解析并运行命令,否则,运行子进程时不会涉及任何 shell。

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

None of those equalities are true, and half of those "shells" aren't actually shells.

Your bash shell is a shell. When you launch your Python script from that shell, the Python process that runs the script is a child process of the bash shell process. When you launch a subprocess from the Python script, that subprocess is a child process of the Python process. If you launch the subprocess with shell=True, Python invokes a shell to parse and run the command, but otherwise, no shell is involved in running the subprocess.

Child processes inherit environment variables from their parent on startup (unless you take specific steps to avoid that), but they cannot set environment variables for their parent. You cannot run a Python script to set environment variables in your shell, or run a subprocess from Python to set your Python script's environment variables.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文