Как я могу записать в стандартный процесс процессов и читать с его стандартного вывода, пока он еще работает? - PullRequest
1 голос
/ 27 марта 2020

Редактировать: я запускаю это на windows, поскольку серверы администрируются на моей основной игровой установке

Я пытаюсь создать контроллер сервера minecraft, чтобы я мог упростить процесс администрирования серверов. Я был в состоянии использовать подпроцесс для запуска и остановки сервера без проблем, однако, когда я пытаюсь заставить его выдавать команды, это не будет. Если я не использую subprocess.communicate(), проблема с этим решением, однако, заключается в том, что он заставляет скрипт ждать вечно и в конечном итоге создает sh, в результате чего сервер работает. Вот код для того, что у меня есть.

import subprocess


class Server:
    def __init__(self, server_start_bat, dir):
        self.server_start_bat = server_start_bat
        self.dir = dir

    def start_server(self):
        self.server = subprocess.Popen(self.server_start_bat, cwd=self.dir, shell=True, stdin=subprocess.PIPE,
                                       universal_newlines=True, text=True)

    def stop_server(self):
        self.run_command('stop')
        self.server.communicate()

    def op_me(self):
        self.run_command(f'op Username')

    def clear_weather(self):
        self.run_command(f'weather clear 1000000')

    def mob_griefing_on(self):
        self.run_command(f'gamerule mobGriefing True')

    def mob_griefing_off(self):
        self.run_command(f'gamerule mobGriefing True')

    def set_time_day(self):
        self.run_command(f'time set day')

    def set_time_night(self):
        self.run_command(f'time set night')

    def run_command(self, command_text):
        self.server.stdin.write(f'{command_text}\n')

1 Ответ

1 голос
/ 27 марта 2020

Редактировать: проверить Неблокирующее чтение для подпроцесса. ТРУБА в python

Я боролся с очень похожей проблемой, и мне потребовалось 3 дня, чтобы все это заработало , Проблема с subprocess.communicate заключается в том, что вы можете вызывать его только один раз, и вывод, по-видимому, дается только после завершения подпроцесса (в моем опыте вполне мог быть ошибочным).

Вопрос i has, к которому сводится ваш вопрос: как я могу записать в стандартный поток процессов и прочитать его стандартный вывод, пока он еще работает?

В итоге я использовал Pipes. Обратите внимание, что они должны быть сброшены на меня, если вы напишите что-то меньшее, чем BUFSIZE, что в моем случае 0x1000. См. man pipe для получения дополнительной информации.

В любом случае, ниже приведен мой код.

import time
import pty
import os
import subprocess
PIPE_BUFSIZE = 4096
_control = False


class Pipe:

    def __init__(self, flags=None, terminal=True):
        """Creates a Pipe you can easily write to and read from. Default is to open up a pseudo-terminal.
            If you supply flags, pipe2 is used."""

        if flags or not terminal:
            self._readfd, self._writefd = os.pipe2(flags)
        else:   # default
            self._readfd, self._writefd = pty.openpty()

        self.readobj = open(self._readfd, "rb", 0)  
        self.writeobj = open(self._writefd, "wb", 0)

    def write(self, text):
        if isinstance(text, str):
            text = text.encode()

        result = self.writeobj.write(text)
        self.writeobj.flush()
        return result

    def read(self, n):
        if _control:    # im not using this anymore since the flush seems to be reliable
            controlstr = b"this_was_flushed"
            controllen = len(controlstr)

            self.writeobj.write(controlstr)
            time.sleep(.001)
            self.writeobj.flush()

            result = self.readobj.read(n+controllen)
            assert result[-controllen:] == controlstr
            return result[:-controllen]
        else:
            self.writeobj.flush()
            return self.readobj.read(n)


class ProcessIOWrapper:
    """Provides an easy way to redirect stdout and stderr using pipes. Write to the processes STDIN and read from STDOUT at any time! """

    def __init__(self, args, inittext=None, redirect=True):

        #create three pseudo terminals
        self.in_pipe = Pipe()
        self.out_pipe = Pipe()
        self.err_pipe = Pipe()

        # if we want to redirect, tell the subprocess to write to our pipe, else it will print to normal stdout
        if redirect:
            stdout_arg= self.out_pipe.writeobj
            stderr_arg= self.err_pipe.writeobj
        else:
            stdout_arg=None
            stderr_arg= None

        self.process = subprocess.Popen(args, stdin=self.in_pipe.readobj, stdout=stdout_arg, stderr=stderr_arg)


    def write(self, text):
        return self.in_pipe.write(text)

    def read(self, n, start=None):
        return self.out_pipe.read(n)




Маленький C Программа, которую я использовал для тестирования (также использовал другие)

#include <stdio.h>


int main(){
    puts("start\n");
    char buf[100]="bufbufbuf";
    while(1){

        gets(buf);
        for (int i=0; i<strlen(buf); i++)
            if (i%2==0) buf[i]+=1;

        puts(buf);
    }
}


Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...