使用 subprocess.Popen 执行 bash

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7206519/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 00:41:11  来源:igfitidea点击:

Executing bash with subprocess.Popen

pythonbashsubprocess

提问by Dany Zatuchna

I'm trying to write a wrapper for a bash session using python. The first thing I did was just try to spawn a bash process, and then try to read its output. like this:

我正在尝试使用 python 为 bash 会话编写一个包装器。我做的第一件事就是尝试生成一个 bash 进程,然后尝试读取它的输出。像这样:

from subprocess import Popen, PIPE
bash = Popen("bash", stdin = PIPE, stdout = PIPE, stderr = PIPE)
prompt = bash.stdout.read()
bash.stdin.write("ls\n")
ls_output = bash.stdout.read()

But this does not work. First, reading from bash's stdout after creating the process fails, and when I try to write to stdin, I get a broken pipe error. What am I doing wrong?

但这不起作用。首先,在创建进程后从 bash 的标准输出读取失败,当我尝试写入标准输入时,出现管道损坏错误。我究竟做错了什么?

Just to clarify again, I'm not interested in running a single command via bash and then retrieving its output, I want to have a bash session running in some process with which I can communicate via pipes.

再次澄清一下,我对通过 bash 运行单个命令然后检索其输出不感兴趣,我希望在某个进程中运行一个 bash 会话,我可以通过管道与之进行通信。

回答by Gabriel Ross

This works:

这有效:

import subprocess
command = "ls"
p = subprocess.Popen(command, shell=True, bufsize=0, stdout=subprocess.PIPE, universal_newlines=True)
p.wait()
output = p.stdout.read()
p.stdout.close()

回答by a3nm

The read()call is blocking, so it reads the output of the first command and hangs because further data could still arrive (ie. bashis still running). To perform a non-blocking read, see Non-blocking read on a subprocess.PIPE in python.

read()呼叫被阻塞,所以它读取所述第一命令和挂起因为进一步的数据仍然可以到达(即输出bash仍在运行)。要执行非阻塞读取,请参阅python 中 subprocess.PIPE 上的非阻塞读取

回答by Jonathan Sternberg

Why don't you just use the cmd module/raw_input along with subprocess.Popen(shell = True)?

为什么不直接使用cmd 模块/raw_input 和 subprocess.Popen(shell = True)?

回答by Liao

You can refer to my another answer: https://stackoverflow.com/a/43012138/3555925, which use pseudo-terminal and select in handle stdin/stdout.

您可以参考我的另一个答案:https: //stackoverflow.com/a/43012138/3555925,它使用伪终端并在句柄 stdin/stdout 中选择。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import sys
import select
import termios
import tty
import pty
from subprocess import Popen

command = 'bash'
# command = 'docker run -it --rm centos /bin/bash'.split()

# save original tty setting then set it to raw mode
old_tty = termios.tcgetattr(sys.stdin)
tty.setraw(sys.stdin.fileno())

# open pseudo-terminal to interact with subprocess
master_fd, slave_fd = pty.openpty()

# use os.setsid() make it run in a new process group, or bash job control will not be enabled
p = Popen(command,
          preexec_fn=os.setsid,
          stdin=slave_fd,
          stdout=slave_fd,
          stderr=slave_fd,
          universal_newlines=True)

while p.poll() is None:
    r, w, e = select.select([sys.stdin, master_fd], [], [])
    if sys.stdin in r:
        d = os.read(sys.stdin.fileno(), 10240)
        os.write(master_fd, d)
    elif master_fd in r:
        o = os.read(master_fd, 10240)
        if o:
            os.write(sys.stdout.fileno(), o)

# restore tty settings back
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty)