Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 防止意外的stdin读取和锁定子进程_Python_Linux_Shell_Pipe_Subprocess - Fatal编程技术网

Python 防止意外的stdin读取和锁定子进程

Python 防止意外的stdin读取和锁定子进程,python,linux,shell,pipe,subprocess,Python,Linux,Shell,Pipe,Subprocess,一个简单的例子,我试图解决所有情况。 我正在运行一个子进程来执行某项任务,我不希望它要求stdin,但在我甚至不希望看到的少数情况下,它可能会尝试读取。 我想防止它在那种情况下被挂起来 下面是一个典型的例子: import subprocess p = subprocess.Popen(["unzip", "-tqq", "encrypted.zip"]) p.wait() 这将永远挂着。 我已经尝试添加 stdin=open(os.devnull) 诸如此类 如果我找到有价值的解决方案,我

一个简单的例子,我试图解决所有情况。 我正在运行一个子进程来执行某项任务,我不希望它要求stdin,但在我甚至不希望看到的少数情况下,它可能会尝试读取。 我想防止它在那种情况下被挂起来

下面是一个典型的例子:

import subprocess
p = subprocess.Popen(["unzip", "-tqq", "encrypted.zip"])
p.wait()
这将永远挂着。 我已经尝试添加

stdin=open(os.devnull)
诸如此类

如果我找到有价值的解决方案,我将发布。 这足以让我在父进程中接收到异常,而不是无休止地挂起通信/等待


更新:问题似乎比我最初预期的更复杂,子进程(在密码和其他情况下)从其他文件描述符(如/dev/tty)中读取,以与shell交互。可能不像我想的那么容易解决。

显然罪魁祸首是直接使用/dev/tty之类的

至少在linux上,一种解决方案是在Popen调用中添加以下参数:

preexec_fn=os.setsid
#!/usr/bin/env python3
import subprocess
import sys

subprocess.check_call([sys.executable, 'ask-password.py'],
                      stdin=subprocess.DEVNULL, start_new_session=True,
                      stderr=subprocess.DEVNULL)
这会导致设置新的会话id,并且不允许直接从tty读取。我可能会使用以下代码(stdinclose只是以防万一):

最后两行可由一次呼叫代替:

p.communicate()
因为communicate()在发送所有提供的输入后关闭stdin文件

看起来简单而优雅

或者:

import subprocess
import os
p = subprocess.Popen(["unzip", "-tqq", "encrypted.zip"],
                     stdin=open(os.devnull), preexec_fn=os.setsid)
p.communicate()

如果您的子进程可能会要求输入密码,那么它可能会在标准输入/输出/错误流之外执行此操作(如果tty可用),请参阅中的第一个原因

同样,创建新会话会阻止子进程使用父进程的tty,例如,如果您有
ask password.py
脚本:

#!/usr/bin/env python
"""Ask for password. It defaults to working with a terminal directly."""
from getpass import getpass

try:
    _ = getpass()
except EOFError:
    pass # ignore
else:
    assert 0
然后,要将其作为子进程调用,使其不会挂起等待密码,可以使用
start\u new\u session=True
参数:

preexec_fn=os.setsid
#!/usr/bin/env python3
import subprocess
import sys

subprocess.check_call([sys.executable, 'ask-password.py'],
                      stdin=subprocess.DEVNULL, start_new_session=True,
                      stderr=subprocess.DEVNULL)
stderr也被重定向到这里,因为
getpass()
使用它作为回退,以打印警告和提示

要在Python 2上的Unix上模拟
start\u new\u session=True
,可以使用
preexec\u fn=os.setsid

或者通过
stdin=PIPE
并立即使用
.communicate()
将其关闭:


注意:您不需要
.communicate()
,除非您使用
子流程.PIPE
<如果使用具有真实文件描述符(
.fileno()
)的对象,例如由
open(os.devnull,…)
返回的对象,则code>check\u call()是完全安全的。重定向发生在执行子进程之前(在
fork()
之后,在
exec()
之前)——没有理由在此处使用
.communicate()
而不是
check\u call()

无关:您可以在Python 3上使用
subprocess.DEVNULL
。我更喜欢
os.setsid
解决方案(如果可行的话),但您也可以使用(您的案例是)提供伪tty。您可以调用
p.communicate()
而不是
p.stdin.close();p、 在第一个示例中,wait()
。在上一个示例中,没有理由调用
p.communicate()
(只需使用
子流程。请检查调用()
)。meta:您可以@J.F.Sebastian awesome,必须查看通信源代码,以查看它在写入输入后关闭stdin。太好了,谢谢@J.F.Sebastian如果我理解正确的话,check_call是一个选项,当您不想从进程中获取stdout/stderr并且只想在失败时得到通知时使用。这也是一个可行的选择——但如果使用check_call,我也必须使用setsid。对的