在 Windows 上具有恒定输出的 Python 无块子进程输入 [英] Python blockless subproccess input with constant output on Windows

查看:63
本文介绍了在 Windows 上具有恒定输出的 Python 无块子进程输入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 subproccess 和 _thread 模块运行命令.子进程有一个输出流.为了解决这个问题,我使用了两个线程,一个不断打印新行,另一个检查输入.当我通过 proc.stdin.write('Some string') 传递 subproccess 输入时,它返回 1,然后我没有输出.按照我读过的大多数其他问题,Communicate 不起作用,因为它阻止等待 EOF,尽管它确实打印了将要返回的任何内容的第一行.我看到了一些使用pty"的解决方案,但 Windows 不支持它.

I am trying to run a command with subproccess and the _thread modules. The subproccess has a stream of output. To combat this I used two threads, one constantly prints new lines and the other is checking for input. When I pass the subproccess input through proc.stdin.write('Some string') it returns 1 and then I get no output. Communicate doesn't work as per most other questions I have read because it blocks waiting for the EOF although it does print the first line of the whatever was going to be returned. I saw a few solutions using 'pty' but it is not supported on Windows.

服务器文件夹中的文件只是一个我的世界服务器,如果你想自己试试.

The file in the server folder is just a minecraft server if you want to try it yourself.

from subprocess import Popen,PIPE
import _thread
import sys
# asdf
proc = None
run = True
stdout = None
stdin = None


def getInput():
    global proc
    global run, stdin, stdout
    print("Proc inside the get input funct"+str(proc))
    inputs = input("Enter Something" + "\n")
    print("YOU ENTERED:", inputs)
    print("ATTEMPTING TO PIPE IT INTO THE CMD")
    run = True

    """----------------------------------------"""
    """        Works but blocks outputs        """
    """----------------------------------------"""
    # out,err=proc.communicate(bytes(inputs,'UTF-8'))
    # proc.stdin.flush()
    # print("Out is: "+out)




    """----------------------------------------"""
    """   Doesn't write but doesn't block      """
    """----------------------------------------"""
    # test = 0
    # test=proc.stdin.write(bytes(inputs,'UTF-8'))
    # print(test)
    # proc.stdin.flush()


def execute(command):
    global proc, stdin, stdout
    proc = Popen(command, cwd='C://Users//Derek//Desktop//server//',stdin=PIPE,stdout=PIPE,stderr=stdout, shell=True)
    lines_iterator = iter(proc.stdout.readline, "")
    print("Proc inside of the execute funct:"+str(proc))
    # print(lines_iterator)
    for line in lines_iterator:
        # print(str(line[2:-1]))
        # if line.decode('UTF-8') != '':
        print(line[:-2].decode('UTF-8')),  # yield line
        sys.stdout.flush()


threadTwo = _thread.start_new_thread(execute, (["java", "-jar", "minecraft_server.jar"], ))

while 1:
    if run and proc!=None:
        run = False
        threadOne = _thread.start_new_thread(getInput, ( ))

    pass

推荐答案

proc.communicate() 等待子进程完成,因此它最多可以使用一次– 您可以一次传递所有输入,并在子进程退出后获取所有输出.

proc.communicate() waits for the subprocess to finish therefore it can be used at most once – you can pass all input at once and get all the output after the child process exits.

如果您不修改输入/输出,则不需要重定向子进程的 stdin/stdout.

If you are not modifying input/output then you do not need to redirect subprocess' stdin/stdout.

将输入提供给后台线程中的子进程,并在其输出到达时逐行打印:

To feed input to a subprocess in a background thread and to print its output as soon as it arrives line-by-line:

#!/usr/bin/env python3
import errno
from io import TextIOWrapper
from subprocess import Popen, PIPE
from threading import Thread

def feed(pipe):
    while True:
        try: # get input
            line = input('Enter input for minecraft')
        except EOFError:
            break # no more input
        else:
            # ... do something with `line` here

            # feed input to pipe
            try:
                print(line, file=pipe)
            except BrokenPipeError:
                break # can't write to pipe anymore
            except OSError as e:
                if e.errno == errno.EINVAL:
                    break  # same as EPIPE on Windows
                else:
                    raise # allow the error to propagate

    try:
        pipe.close() # inform subprocess -- no more input
    except OSError:
        pass # ignore

with Popen(["java", "-jar", "minecraft_server.jar"],
           cwd=r'C:\Users\Derek\Desktop\server',
           stdin=PIPE, stdout=PIPE, bufsize=1) as p, \
     TextIOWrapper(p.stdin, encoding='utf-8', 
                   write_through=True, line_buffering=True) as text_input:
    Thread(target=feed, args=[text_input], daemon=True).start()
    for line in TextIOWrapper(p.stdout, encoding='utf-8'):
        # ... do something with `line` here
        print(line, end='')

关于p.stdin的注意事项:

  1. print() 在每个 line 的末尾添加一个换行符.这是必要的,因为 input() 去掉换行符
  2. p.stdin.flush() 在每行之后调用 (line_buffering=True)
  1. print() adds a newline at the end of each line. It is necessary because input() strips the newline
  2. p.stdin.flush() is called after each line (line_buffering=True)

Minecraft 的输出可能会延迟,直到其 stdout 缓冲区被刷新.

The output from minecraft may be delayed until its stdout buffer is flushed.

如果您在 do something with line here" 注释周围没有什么可添加的,那么不要重定向相应的管道(暂时忽略字符编码问题).

If you have nothing to add around the "do something with line here" comments then do not redirect corresponding pipes (ignoring character encoding issues for a moment).

TextIOWrapper 默认使用通用换行模式.如果您不想要,请明确指定 newline 参数.

TextIOWrapper uses the universal newline mode by default. Specify newline parameter explicitly if you do not want that.

这篇关于在 Windows 上具有恒定输出的 Python 无块子进程输入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆