Python,bash/python/子进程进程(shell)之间的关系?
当尝试用 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 代理吗?
问题
使用上述命名法和相等性测试
BASH_PROCESS
==PYTHON_SUBPROCESS
吗?- 是
PYTHON_SUBPROCESS
==SUBPROCESS_SUBPROCESS
吗? - 是
PYTHON_SUBPROCESS
==SUBPROCESS_SUBPROCESS=True
吗? - 如果
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 runpython3 foo.py
, the python script runs in sayPYTHON_SUBPROCESS
- Within
foo.py
is a call tosubprocess.run(...)
, this subprocess command runs in say `SUBPROCESS_SUBPROCESS - Within
foo.py
issubprocess.run(..., shell=True)
, this subprocess command runs in saySUBPROCESS_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
- Is
BASH_PROCESS
==PYTHON_SUBPROCESS
? - Is
PYTHON_SUBPROCESS
==SUBPROCESS_SUBPROCESS
? - Is
PYTHON_SUBPROCESS
==SUBPROCESS_SUBPROCESS=True
? - If
SUBPROCESS_SUBPROCESS=True
is not equal toBASH_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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您似乎在这里混淆了几个概念。
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=
关键字参数来指向该环境您希望它使用:或让 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 youexec 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, theinit
process.)subprocess
runs a subprocess, simply. Ifshell=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 ofprintf
. Whenprintf
terminates, so doessh
, 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 subprocesssubprocess.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 thesubprocess
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: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 ofeval
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 scriptfoo.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".
这些等式都不是真的,而且这些“贝壳”有一半实际上并不是贝壳。
您的 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.