简体   繁体   English

如何在 python 中广泛和/或交替地从 stdout 和/或 stderr 读取并写入进程的 stdin?

[英]How to wildly and/or alternatingly read from stdout and/or stderr and write to stdin of a process in python?

I searched here a lot of different questions and answers, but I did not found a general approach for:我在这里搜索了很多不同的问题和答案,但我没有找到一个通用的方法:

  • reading from stdout and stderr, what is available - until the last byte (currently!) available (even if it is not a \n)从 stdout 和 stderr 读取可用的内容 - 直到最后一个字节(当前!)可用(即使它不是 \n)
  • depending upon the read information write something to stdin根据读取的信息向标准输入写入内容
  • the command line tool will react to this stdin input and write (much later) something/nothing命令行工具将对这个标准输入做出反应并(很久以后)写一些东西/什么都没有
  • start from beginning - or leave loop if process has finished, capturing its return code从头开始 - 如果进程完成则离开循环,捕获其返回码

Most examples here findable write only ONCE to stdin and read only ONCE (before/afterwards) from stdout and/or stderr.此处可找到的大多数示例仅将 ONCE 写入 stdin,而从 stdout 和/或 stderr 仅读取 ONCE(之前/之后)。 My intention is to "weave" reading from stdout and/or stderr and writing to stdin!我的意图是“编织”从标准输出和/或标准错误读取并写入标准输入! Here an example:这里有一个例子:

  • starting a command line tool (finally with parameters) - eg python3.exe启动命令行工具(最后带有参数) - 例如python3.exe
  • reading always from the stdout and stderr总是从标准输出和标准错误读取
  • eg read everything and after reading >>> from stdout例如阅读所有内容,然后从标准输出阅读>>>
  • write print('Hello World.')\nprint('Hello World.')\n
  • eg read everything ( Hello World.\n )and after reading >>> from stdout例如阅读所有内容( Hello World.\n )并从标准输出阅读>>>
  • write x = [6, 0]\nx = [6, 0]\n
  • eg read everything and after reading >>> from stdout例如阅读所有内容,然后从标准输出阅读>>>
  • write y = x[0] / x[1]\ny = x[0] / x[1]\n
  • eg read everything (... ZeroDivisionError: division by zero on stdout/stderr)例如读取所有内容(... ZeroDivisionError: division by zero
  • ... ...

I tried to solve it with this found internet example (after other failed attempts):我试图用这个找到的互联网示例来解决它(在其他失败的尝试之后):

# Example #27
# of https://www.programcreek.com/python/example/85342/asyncio.create_subprocess_shell
# Source Project: Python-Journey-from-Novice-to-Expert   Author: PacktPublishing   File: 07_processes.py    License: MIT License    5 votes vote downvote up
import asyncio
import sys

async def read_from_pipe(pipe, buf, timeout_sec):
    while True:
        try:
            pipe_byte = await asyncio.wait_for(pipe.read(1), timeout_sec)
        except asyncio.TimeoutError:
            break
        else:
            if len(pipe_byte) == 1:
                buf.append(pipe_byte[0])
            else:
                pipe_byte == b'\n' # in case of end of file: fake end of line 
            if pipe_byte == b'\n':
                return len(buf)

async def run_script(version):
    process = await asyncio.create_subprocess_shell(
        r'C:\Programs\Python\Python38-32\python.exe',
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE,
        stdin=asyncio.subprocess.PIPE,
    )

    if version == 0:
        # Write a simple Python script to the interpreter
        process.stdin.write(b'\n'.join((
            b'import math',
            b'x = 2 ** 8',
            b'y = math.sqrt(x)',
            b'z = math.sqrt(y)',
            b'print("x: %d" % x)',
            b'print("y: %d" % y)',
            b'print("z: %d" % z)',
            b'for i in range(int(z)):',
            b'    print("i: %d" % i)',
        )))
        # Make sure the stdin is flushed asynchronously
        await process.stdin.drain()
        # And send the end of file so the Python interpreter will
        # start processing the input. Without this the process will
        # stall forever.
        process.stdin.write_eof()

        # Fetch the lines from the stdout asynchronously
        async for out in process.stdout:
            # Decode the output from bytes and strip the whitespace
            # (newline) at the right
            print(out.decode('utf-8').rstrip())

        # Wait for the process to exit
        await process.wait() 
    elif version == 1:
        cmds = [b'import math',
          b'x = 2 ** 8',
          b'y = math.sqrt(x)',
          b'z = math.sqrt(y)',
          # b'q = z / 0',
          b'print("x: %d" % x)',
          b'print("y: %d" % y)',
          b'print("z: %d" % z)',
          b'for i in range(int(z)):',
          b'    print("i: %d" % i)',
          b'exit(0)',
        ]
        idx = 0
        while True:
            stdout_buf = bytearray(b'')           
            out_read = await read_from_pipe(process.stdout, stdout_buf, 0.5)
            print(f'stdout[{out_read}]: {stdout_buf.decode("ascii")}\n') if out_read else None
            stderr_buf = bytearray(b'')             
            err_read = await read_from_pipe(process.stderr, stderr_buf, 0.5)
            print(f'stderr[{err_read}]: {stdout_buf.decode("ascii")}\n') if err_read else None
            if idx < len(cmds):
                current_cmd = cmds[idx].decode('ascii')
                print(f'writing command at index {idx}: "{current_cmd}"')
                process.stdin.write(cmds[idx])
                process.stdin.write(b'\n')
                await process.stdin.drain()
                process.stdin.write_eof() # tried with/without this line, afterwards program hangs
                idx += 1
            else:
                break
        await process.wait()             

if sys.platform == "win32":
    codepage = 'cp437'
    loop = asyncio.ProactorEventLoop() # For subprocess' pipes on Windows
    asyncio.set_event_loop(loop)
else:
    codepage = 'utf-8'
    loop = asyncio.get_event_loop()

version = 1 # version = 0 runs but is not alternatingly reading stdout/stderr and writing to stdin!
returncode = loop.run_until_complete(run_script(1))
print(f'done with return code = {returncode}.')

Currently it doesn't read anything from the stdout and stderr .目前它不会从stdoutstderr读取任何内容。 And after the entries in cmds are written, program hangs too.并且在cmds中的条目写入后,程序也会挂起。 Finally it should run under linux.最后它应该在 linux 下运行。

How do I write the program correctly?如何正确编写程序?

Is python3.exe a "too special" command line tool and is the root cause of these problems? python3.exe 是不是一个“太特殊”的命令行工具,是这些问题的根本原因吗?

Hint: This example and the solution do not have to be performant at all.提示:这个例子和解决方案根本不必是高性能的。 The intended command line tool to control is quite slow (overall execution 20 s to 20 min).用于控制的预期命令行工具非常慢(总体执行时间为 20 秒到 20 分钟)。 Multithreading and multiprocessing is not really required, if not needed for a (simplified) working solution.如果(简化的)工作解决方案不需要多线程和多处理,则实际上并不需要多线程和多处理。

I found out that python3.exe is a bit too special to control.我发现python3.exe有点太特殊了,无法控制。 I better use eg cmd /S on windows (I read /bin/bash for Linux) - this works now:我最好在 windows 上使用例如cmd /S (我为 Linux 阅读了/bin/bash ) - 现在可以使用:

# Example #27
# of https://www.programcreek.com/python/example/85342/asyncio.create_subprocess_shell
# Source Project: Python-Journey-from-Novice-to-Expert   Author: PacktPublishing   File: 07_processes.py    License: MIT License    5 votes vote downvote up
import asyncio
import sys

async def read_from_pipe(pipe, buf, timeout_sec):
    while True:
        try:
            pipe_byte = await asyncio.wait_for(pipe.read(1), timeout_sec)
        except asyncio.TimeoutError:
            return len(buf) # no more bytes available currently on that pipe
        else:
            if len(pipe_byte) == 1:
                buf.append(pipe_byte[0])
            else:
                return len(buf) # end of pipe reached

async def run_script():
    process = await asyncio.create_subprocess_shell(
        'cmd /S',
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE,
        stdin=asyncio.subprocess.PIPE,
    )

    cmds = [b'dir P*C*S*.*',
    b'echo %temp%',
    b'exit']
    idx = 0
    while True:
        stdout_buf = bytearray(b'')           
        out_read = await read_from_pipe(process.stdout, stdout_buf, 0.5)
        print(f'stdout[{out_read}]: {stdout_buf.decode("ascii")}\n') if out_read else None
        stderr_buf = bytearray(b'')             
        err_read = await read_from_pipe(process.stderr, stderr_buf, 0.5)
        print(f'stderr[{err_read}]: {stdout_buf.decode("ascii")}\n') if err_read else None
        if idx < len(cmds):
            current_cmd = cmds[idx].decode('ascii')
            print(f'writing command at index {idx}: "{current_cmd}"')
            process.stdin.write(cmds[idx])
            process.stdin.write(b'\n')
            await process.stdin.drain()
            idx += 1
        else:
            pass
        if process.returncode is not None:
            print(f'return code = {process.returncode}')
            return process.returncode

if sys.platform == "win32":
    codepage = 'cp437'
    loop = asyncio.ProactorEventLoop() # For subprocess' pipes on Windows
    asyncio.set_event_loop(loop)
else:
    codepage = 'utf-8'
    loop = asyncio.get_event_loop()

returncode = loop.run_until_complete(run_script())
print(f'done with return code = {returncode}.')

The output is on my computer: output 在我的电脑上:

PS C:\Git\ownPythonRepository\Python\CliTap>  c:; cd 'c:\Git\ownPythonRepository\Python\CliTap'; & 'C:\Programs\Python\Python38-32\python.exe' 'c:\Users\BitLauncher\.vscode\extensions\ms-python.python-2022.14.0\pythonFiles\lib\python\debugpy\adapter/../..\debugpy\launcher' '63136' '--' 'c:\Git\ownPythonRepository\Python\CliTap\PythonConsoleSandbox.py' 
stdout[137]: Microsoft Windows [Version 10.0.11111.2222]
(c) Microsoft Corporation. All rights reserved.

C:\Git\ownPythonRepository\Python\CliTap>

 stdout[340]: dir P*C*S*.*
 Volume in drive C is What
 Volume Serial Number is 9999-9999

 Directory of C:\Git\ownPythonRepository\Python\CliTap

2022-09-26  23:52             2,365 PythonConsoleSandbox.py
               1 File(s)          2,365 bytes
               0 Dir(s)  99,999,999,999 bytes free

C:\Git\ownPythonRepository\Python\CliTap>
writing command at index 1: "echo %temp%"
stdout[93]: echo %temp%
C:\Users\BitLau~1\AppData\Local\Temp

C:\Git\ownPythonRepository\Python\CliTap>

writing command at index 2: "exit"
stdout[5]: exit


return code = 1
done with return code = 1.
PS C:\Git\ownPythonRepository\Python\CliTap> 

That's it - now I will be able to write depending upon stdout and/or stderr specific commands to stdin... great.就是这样 - 现在我将能够根据 stdout 和/或 stderr 特定命令向 stdin 写入......太好了。 Later I can improve it by multithreading:-) if needed.稍后我可以通过多线程改进它:-) 如果需要。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 重复写入stdin并从python中读取进程的stdout - Repeatedly write to stdin and read from stdout of a process from python Python asyncio子进程连续写入stdin和读取stdout / stderr - Python asyncio subprocess write stdin and read stdout/stderr continuously python asyncio如何读取StdIn并写入StdOut? - python asyncio how to read StdIn and write to StdOut? 如何在 Python 中“捕获”程序的 stdin、stdout、stderr? - How to 'catch' stdin, stdout, stderr of a program in Python? 从 Python 中的 stdin、stderr 读取 - Read from stdin, stderr in Python 在python中长时间运行的子进程上写入stdin并从stdout读取 - write to stdin and read from stdout on long-running child process in python communic()和.stdin.write,.stdout.read或.stderr.read - python之间的区别 - difference between communicate() and .stdin.write, .stdout.read or .stderr.read - python 如何使用独立的stdout,stderr和stdin来分叉新进程? - How do I fork a new process with independent stdout, stderr, and stdin? 如何运行只能写入STDOUT并从STDIN读取的脚本? - How to run a script that can only write to STDOUT and read from STDIN? 如何将参数从python内部传递到stdin,stdout,stderr中的远程ssh命令 - How to pass arguments from inside a python to a remote ssh command in stdin, stdout, stderr
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM