简体   繁体   中英

Python subprocess hangs on interaction

I am trying to write a minecraft server wrapper that allows me to send it commands and receive output. Eventually, I'll attach a socket interface so that I can control my home server remotely to restart / second commands / etc.

To this end, I am attempting to use the python subprocess module to start the server, then send commands and receive the output of the server. Right now, I am running into an issue I can grab the output of the server and reflect it to screen, but the very first command I send to the process freezes the whole thing and I have to kill it. It should be noted that I have attempted to remove the process.communicate line and instead replaced it with a print(command). This also froze the process My very basic current code is as follows:

from subprocess import Popen, PIPE
from threading import Thread
import threading

def listen(process):
    while process.poll() is None:
        output = process.stdout.readline()
        print(str(output))

def talk(process):
    command = input("Enter command: ")
    while command != "exit_wrapper":
        #freezes on first send command
        parse_command(process, command)
        command = input("Enter command: ")

    print("EXITTING! KILLING SERVER!")
    process.kill()

def parse_command(process, command):
    process.communicate(command.encode())

def main():
    process = Popen("C:\\Minecraft Servers\\ServerStart.bat", cwd = "C:\\Minecraft Servers\\", stdout=PIPE, stdin=PIPE)

    listener = Thread(None, listen, None, kwargs={'process':process})
    listener.start()

    talker = Thread(None, talk, None, kwargs={'process':process})
    talker.start()

    listener.join()
    talker.join()

if __name__ == "__main__":
    main()

Any help offered would be greatly appreciated!

subprocess.Popen.communicate() documentation clearly states:

Interact with process: Send data to stdin. Read data from stdout and stderr, until end-of-file is reached. .

And in your case it's doing exactly that. What you want to do instead of waiting for the process to be terminated is to interact with the process so, much like you're reading from the STDOUT stream directly in your listen() function, you should write to the process STDIN in order to send it commands. Something like:

def talk(process):
    command = input("Enter command: ")
    while command != "exit_wrapper" and process.poll() is None:
        process.stdin.write(command)  # write the command to the process STDIN
        process.stdin.flush()  # flush it
        command = input("Enter command: ")  # get the next command from user
    if process.poll() is None:
        print("EXITING! KILLING SERVER!")
        process.kill()

The problem with this approach, however, is that you'll have a potential of overwriting the server's output with your Enter command: prompt and that the user will end up typing the command over the server's output instead in the 'prompt' you've designated.

What you might want to do instead is to parse your server's output in the listen() function and then based on the collected output determine when the wrapped server expects user input and then, and only then, call the talk() function (of course, remove the while loop from it) to obtain user input.

You should also pipe-out STDERR as well, in case the Minecraft server is trying to tell you something over it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM