在 Windows 上具有恒定输出的 Python 无块子进程输入 [英] Python blockless subproccess input with constant output on Windows
问题描述
我正在尝试使用 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
的注意事项:
print()
在每个line
的末尾添加一个换行符.这是必要的,因为input()
去掉换行符p.stdin.flush()
在每行之后调用 (line_buffering=True
)
print()
adds a newline at the end of eachline
. It is necessary becauseinput()
strips the newlinep.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屋!