python - 防止意外的标准输入读取并锁定子进程

标签 python linux shell pipe subprocess

我试图解决所有情况的一个简单案例。 我正在运行一个子进程来执行某项任务,我不希望它请求标准输入,但在极少数情况下,我什至没有预料到,它可能会尝试读取。 我想防止它在那种情况下挂起。

这是一个经典的例子:

import subprocess
p = subprocess.Popen(["unzip", "-tqq", "encrypted.zip"])
p.wait()

这将永远挂起。 我已经尝试添加

stdin=open(os.devnull)

等等..

如果我找到有值(value)的解决方案,将会发布。 足以让我在父进程中收到异常 - 而不是无休止地卡在通信/等待上。

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

最佳答案

如果您的子进程可能要求输入密码,那么如果 tty 可用,它可能会在标准输入/输出/错误流之外执行此操作,参见 Q: Why not just use a pipe (popen())? 中的第一个原因。

作为you've noticed ,创建一个新 session 会阻止子进程使用父进程的 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_new_session=True 参数:

#!/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_new_session=True,您可以使用 preexec_fn=os.setsid

To emulate subprocess.DEVNULL on Python 2, you could use DEVNULL=open(os.devnull, 'r+b', 0)或传递 stdin=PIPE 并使用 .communicate() 立即关闭它:

#!/usr/bin/env python2
import os
import sys
from subprocess import Popen, PIPE

Popen([sys.executable, 'ask-password.py'],
      stdin=PIPE, preexec_fn=os.setsid,
      stderr=PIPE).communicate() #NOTE: assume small output on stderr

注意:除非使用 subprocess.PIPE,否则不需要 .communicate()。如果您使用带有真实文件描述符 (.fileno()) 的对象,例如 open(os.devnull, . .)。重定向发生在子进程执行之前(fork()之后,exec()之前)——没有理由使用.communicate() 而不是 check_call()

关于python - 防止意外的标准输入读取并锁定子进程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33277452/

相关文章:

python - 如何获取一个类的所有方法及其参数的列表

python - 如何动态改变kivy python中的 Canvas 颜色?

linux - 在不重新启动浏览器的情况下从可执行文件安装外部 Chrome 扩展

linux - 使用 ${1 :1} in bash

python - 使用 win32com.client 的 AppActivate 根据 ID 将焦点设置到窗口

python - Tensorflow 错误 - 参数单元不是 rnn 单元,缺少属性,方法是必需的,不可调用

linux - 将 2 个输出显示为 2 个单独的列 |狂欢

c++ - 在实时 Fedora 上编译 C++ 程序的命令

shell - &! 和有什么区别?和&|在 zsh 中?

bash - 有没有办法检查是否使用 -x 标志执行 shell 脚本