Entrada de subproceso sin locking de Python con salida constante en Windows

Estoy intentando ejecutar un comando con subproccess y los módulos _thread. El subproceso tiene un flujo de salida. Para combatir esto, utilicé dos hilos, uno imprime constantemente nuevas líneas y el otro está comprobando la entrada. Cuando paso la entrada de subproceso a través de proc.stdin.write('Some string') , devuelve 1 y luego no obtengo salida. Communicate no funciona según la mayoría de las otras preguntas que he leído porque bloquea la espera del EOF, aunque sí imprime la primera línea de lo que se va a devolver. Vi algunas soluciones usando ‘pty’ pero no es compatible con Windows.

El archivo en la carpeta del servidor es solo un servidor de Minecraft si quiere probarlo usted mismo.

 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() espera a que finalice el subproceso, por lo que puede usarse a lo sumo una vez ; puede pasar toda la entrada a la vez y obtener toda la salida después de que finalice el proceso secundario.

Si no está modificando la entrada / salida, no necesita redireccionar la entrada / salida estándar del subproceso.

Para alimentar la entrada a un subproceso en un subproceso en segundo plano e imprimir su salida tan pronto como llega línea por línea:

 #!/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='') 

Nota sobre p.stdin :

  1. print() agrega una nueva línea al final de cada line . Es necesario porque input() elimina la nueva línea.
  2. p.stdin.flush() se llama después de cada línea ( line_buffering=True )

La salida de Minecraft se puede retrasar hasta que se vacíe su búfer stdout.

Si no tiene nada que agregar a los comentarios de “haga algo con la line aquí” , no redirija los conductos correspondientes (ignorando los problemas de encoding de caracteres por un momento).

TextIOWrapper usa el modo de nueva línea universal de forma predeterminada. Especifique el parámetro de newline explícitamente si no quiere eso.