Stdin control over subprocess

huangapple go评论62阅读模式
英文:

Stdin control over subprocess

问题

我需要一个用于控制树莓派上服务器软件的程序,但我卡在如何为“被控制”进程提供标准输入(stdin)的地方,比如Minecraft服务器,它有一个控制台。在这种情况下,我需要一种方法将“刚返回的”值传递给stdin。我已经尝试过使用PIPEs、临时文件,但不起作用。使用一个简单的Python脚本(例如print(input())),它告诉我EOFError: 在读取时发生EOF。我不知道为什么,临时文件对stdout和stderr运行正常。以下是我的代码:

import shlex
import subprocess
import tempfile
import time

class Process():
    def __init__(self, run_command) -> None:
        self.command = run_command
        self._cmd_list = shlex.split(self.command)

    def start(self):
        self._stdout = tempfile.NamedTemporaryFile("w+", encoding="utf+8")
        self._stdin = tempfile.NamedTemporaryFile("a+", encoding="utf+8")
        self._p = subprocess.Popen(self._cmd_list, stdout=self._stdout, stderr=self._stdout, stdin=self._stdin, text=True)

    def get_output(self):
        return self._stdout.read()
    
    def send_input(self, char, newline=True):
        self._stdin.write(char)
        if newline:
            self._stdin.write("\n")
        self._stdin.flush()
    
    def end(self):
        self._p.kill()
        self._stdout.seek(0)
        self.out = self._stdout.read()
        self._stdout.close()
        return self.out
    

if __name__ == "__main__":
    # 运行测试
    p = Process("python3 test.py")
    p.start()
    time.sleep(0.5)
    p.send_input(input())
    time.sleep(0.5)
    print(p.end())

其中test.py是上面的简单脚本。

英文:

I need a program for controlling server software on a raspberry pi, but I'm stuck on how to provide the stdin for the "controlled" process, such as a Minecraft server, which has a console. In this case, I would need a way to pass "just returned" values into stdin. I've tried PIPEs, tempfiles, doesn't work. With a simple python script (eg. print(input()) ) it told me EOFError: EOF while reading
I have no idea why, the tempfiles worked fine for stdout and stderr.
Here's my code:

import shlex
import subprocess
import tempfile
import time

class Process():
    def __init__(self, run_command) -> None:
        self.command = run_command
        self._cmd_list = shlex.split(self.command)

    def start(self):
        self._stdout = tempfile.NamedTemporaryFile("w+", encoding="utf+8")
        self._stdin = tempfile.NamedTemporaryFile("a+", encoding="utf+8")
        self._p = subprocess.Popen(self._cmd_list, stdout=self._stdout, stderr=self._stdout, stdin=self._stdin, text=True)

    def get_output(self):
        return self._stdout.read()
    
    def send_input(self, char, newline=True):
        self._stdin.write(char)
        if newline:
            self._stdin.write("\n")
        self._stdin.flush()
    
    def end(self):
        self._p.kill()
        self._stdout.seek(0)
        self.out = self._stdout.read()
        self._stdout.close()
        return self.out
    

if __name__ == "__main__":
    # run tests
    p = Process("python3 test.py")
    p.start()
    time.sleep(0.5)
    p.send_input(input())
    time.sleep(0.5)
    print(p.end())


#where test.py is the simple script above.


</details>


# 答案1
**得分**: 2

> tempfiles, doesn't work. With a simple python script (eg. print(input()) ) it told me EOFError: EOF while reading I have no idea why

临时文件,不起作用。使用一个简单的Python脚本(例如:print(input())),它告诉我EOFError:在读取时出现EOF,我不知道为什么。

That's pretty straightforward: you run a program, that program expects data on stdin, it reads the entire file, which is empty, and is done.

这很简单:你运行一个程序,该程序期望从stdin读取数据,它读取整个文件,但文件为空,所以读取完成。

A file is data at rest, it does not "wait" on the data producer, and can't tell the consumer to do so either (save if both consumer and producer use advisory locks maybe).

文件是静态数据,它不会“等待”数据生成者,也无法告诉消费者这样做(除非消费者和生成者都使用咨询锁,也许会有不同情况)。

> the tempfiles worked fine for stdout and stderr.

临时文件在stdout和stderr方面运行得很好。

Well of course, the program writes to those as it needs to, as long as they remain available for writing it's happy.

当然,只要这些文件保持可写状态,程序就会将数据写入它们,程序会很满意。

> I've tried PIPEs

我尝试了管道(PIPEs)。

You've probably made a mistake then. Assuming the subcommand does take input over stdin (which isn't always the case mind) this is the normal way to interact.

那你可能犯了错误。假设子命令确实接受stdin输入(这并不总是如此),这是正常的交互方式。

Note though that lots of programs have timing or handling issues when you send stuff to stdin in bulk if they're designed to be *interactive*, they might assume they're receiving one command at a time and not try to process subsets of the buffer. That is why tools like `expect` and the `pexpect` Python library exist, to replicate the back and forth of an interactive session where you send a single command, flush, then wait for the prompt or result to come back before resuming.

请注意,许多程序在批量发送内容到stdin时可能会有时间或处理问题,如果它们被设计为*交互式*,它们可能会假定它们一次只接收一个命令,并且不尝试处理缓冲区的子集。这就是为什么存在像`expect`和`pexpect` Python库这样的工具,用于模拟交互会话的来回过程,在这里你发送一个命令,刷新,然后等待提示或结果返回,然后再继续。

<details>
<summary>英文:</summary>

&gt; tempfiles, doesn&#39;t work. With a simple python script (eg. print(input()) ) it told me EOFError: EOF while reading I have no idea why

That&#39;s pretty straightforward: you run a program, that program expects data on stdin, it reads the entire file, which is empty, and is done.

A file is data at rest, it does not &quot;wait&quot; on the data producer, and can&#39;t tell the consumer to do so either (save if both consumer and producer use advisory locks maybe).

&gt; the tempfiles worked fine for stdout and stderr.

Well of course, the program writes to those as it needs to, as long as they remain available for writing it&#39;s happy.

&gt; I&#39;ve tried PIPEs

You&#39;ve probably made a mistake then. Assuming the subcommand does take input over stdin (which isn&#39;t always the case mind) this is the normal way to interact.

Note though that lots of programs have timing or handling issues when you send stuff to stdin in bulk if they&#39;re designed to be *interactive*, they might assume they&#39;re receiving one command at a time and not try to process subsets of the buffer. That is why tools like `expect` and the `pexpect` Python library exist, to replicate the back and forth of an interactive session where you send a single command, flush, then wait for the prompt or result to come back before resuming.

</details>



huangapple
  • 本文由 发表于 2023年7月3日 20:45:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/76604872.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定