- 关于此问题当前状态的介绍性说明:*
尽管给出了有帮助的答案和许多有帮助的评论,但此问题仍需要一个解释所观察到的行为的实际技术原因的答案,而不是仅提供高级解释和猜测,而没有详细了解幕后发生的情况,以及哪些脚本语言设计决策或系统属性导致允许子进程在脚本退出后完成其工作。
换句话说,一个好的答案至少应该给予一些与以下细节相关的信息:
- 观察到的行为是行为的一般规则还是由于sudo或Popen的特殊性质而引起的例外?或者换句话说:观察到的行为是由Python脚本语言的设计引起的,还是由操作系统的设计引起的(无法关闭子进程,以防止其在父进程关闭时执行和完成仍挂起的任务)
另请参阅另外两个问题,它们涉及本问题的另一个方面,以更深入地了解本问题的背景:
How to reliably check from Python script code if just created directory exist?
Why does a later Python code line win the race of becoming executed before similar preceding line?
下面的代码在文件系统的根目录下创建了一个目录,为了能够这样做,它需要root权限,因此相应的代码行使用sudo
并提供root密码。
现在,事实证明,创建目录的代码行的执行要等到脚本完成,这会导致代码检查前几行代码创建的目录是否存在,并报告该目录未创建。
from subprocess import Popen, PIPE
from os.path import isdir
from time import sleep
sudoShCmd = Popen(["sudo", "-S", "mkdir", "/AnewDir"], stdin=PIPE)
sudoShCmd.stdin.write(b'YOUR ROOT PASSWORD HERE')
sleep(5); print()
print(f'{isdir("/AnewDir")=}')
因为我知道我可以修改提供的代码,以保持其行的执行顺序,我只想了解它是如何来的,在上面的代码的情况下,最后一行是在前一行之前执行,似乎是在最后执行,甚至可能在脚本关闭/退出之后。
所以我的问题是:为什么创建目录的Python代码在脚本末尾的代码之后执行?
为了更清楚地说明我的问题是关于这里一些进一步澄清声明:
我不想知道如何摆脱观察到的行为。我知道如何编写按预期工作的代码。我所追求的是理解在上述代码的情况下实际发生了什么,以便代码行以相反的顺序执行。
请看我关于同一主题的另一个问题,目前的结果是问这个问题,以避免在一个问题中问多个问题:How to reliably check from Python script code if just created directory exist? .
这里还有一些关于评论中给出的解释的想法:
当脚本结束时,Python开始清理,运行sudo的子进程仍然挂起等待输入。
刷新stdin的内容并关闭stdin管道在这里没有任何帮助,所以sudo在获得其密码输入后没有继续进行一定有其他原因。
当python开始关闭它的对象时,stdin关闭,子进程最终获得控制权并开始并行运行,你的脚本没有注意到它自己的子进程正在等待数据,因为脚本没有等待进程完成。
同样,对于关闭的标准输入,执行顺序将保持切换,因此未关闭的标准输入无法解释观察到的行为。
2条答案
按热度按时间wz8daaqr1#
当你运行一个使用PIPE作为输入的子进程时,那个通道从面向行切换到面向块。你的密码卡在管道中,sudo子进程挂起等待数据。你没有等待进程完成,所以你没有注意到挂起。当你的进程退出时,它的对象运行它们的删除代码。这包括关闭管道,刷新密码和让子进程运行。
你可以用
subprocess.run
来代替,它可以传递输入,关闭管道,等待进程完成,这样做的好处是你不需要猜测进程完成的睡眠时间。sd2nnvve2#
让我们总结一下另一个答案的结果,以及对这个问题的大量评论,这些评论使这个答案成为可能(感谢所有贡献者)。希望这个想法是正确的,但如果不是,我很乐意在评论中听到这个答案的缺陷。
调用Popen()会产生一个并行进程,脚本会继续执行下一行代码。现在有两个并行的执行行,它们可以在一定程度上相互独立,因此完成代码行执行的顺序可以颠倒,您在问题中提供的代码是什么情况?
有两种方法可以确保保留代码行的执行顺序:
如果您只注解掉.close()行,您可能会遇到脚本将“挂起”等待,并且根本不执行。如果您注解掉.wait()行,即使stdin PIPE被显式关闭,您仍会遇到相同的奇怪行为,即颠倒执行顺序。
with
结构,该结构负责所有工作,如下所示:另一种实现相同效果的简单方法是使用带有选项shell=True的
subprocess.run()
,将密码回显到sudo的stdin:2>/dev/null
部分隐藏了转到stderr的sudo的输出,因此您只能看到print语句作为输出的效果。