代码之家  ›  专栏  ›  技术社区  ›  Floren

打印子进程stdout行输出

  •  0
  • Floren  · 技术社区  · 2 年前

    我创建了一个简单的Python函数:

    import subprocess
    from io import TextIOWrapper
    
    
    def run_shell_command(command: list, debug: bool = False):
        '''
        Run shell command
    
        :param command: Shell command
        :param debug: Debug mode
        :return: Result code and message
        '''
        try:
            process = subprocess.run(
                command, check=True, text=True, timeout=5,
                stdout=subprocess.PIPE, stderr=subprocess.STDOUT
            )
            if debug:
                for line in TextIOWrapper(process.stdout, encoding='utf-8'):
                    print(line)
            message = 'Shell command executed sucessfully'
            return ({'code': 200, 'msg': message, 'stdout': process.stdout})
        except subprocess.CalledProcessError as e:
            return ({'code': 500, 'msg': e.output})
    
    
    if __name__ == "__main__":
        command = run_shell_command(['ls', '-lah'], True)
        print(command)
    

    当我在调试模式下运行它时,我得到以下错误:

    Traceback (most recent call last):
      File "/tmp/command.py", line 28, in <module>
        command = run_shell_command(['ls', '-lah'], True)
      File "/tmp/command.py", line 19, in run_shell_command
        for line in TextIOWrapper(process.stdout, encoding="utf-8"):
    AttributeError: 'str' object has no attribute 'readable'
    

    在Linux服务器上运行Python 3.9,我想知道你是否能提供一些问题所在的见解。在禁用调试的情况下,我得到了一个正确的文本输出。谢谢你的帮助。

    编辑:根据下面的评论,修复很简单:

            if debug:
                print(process.stdout.rstrip())
    

    然而,与原始规范相比,国际海事组织接受的解决方案更好。

    1 回复  |  直到 2 年前
        1
  •  1
  •   AKX Bryan Oakley    2 年前

    不幸的是,“简单”并不能解决所有涉及的极端情况,您需要阅读子流程 stdout 在流媒体播放时,将其打印出来 将其累积在缓冲区中,并跟踪时间,以便正确超时。请注意,如果4096字节的读取恰好以多行字符结尾,这也可能存在错误(尽管不是很危险)。

    def run_shell_command(command: list, debug: bool = False, timeout: float = 5):
        """
        Run shell command
    
        :param command: Shell command
        :param debug: Debug mode
        :return: Result code and message
        """
        with subprocess.Popen(
            command,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
        ) as process:
            start_time = time.time()
            output = b""
            while True:
                buf = process.stdout.read(4096)
                if debug:
                    # could fail if `buf` happens to end in a multi-byte character
                    print(buf.decode("utf-8", "ignore"))
                output += buf
    
                if time.time() - start_time > timeout:
                    process.kill()
                    message = "Shell command timed out"
                    return {"code": 500, "msg": message, "stdout": output}
    
                if process.poll() is not None:
                    break
        if process.returncode != 0:
            message = "Shell command failed"
            return {"code": 500, "msg": message, "stdout": output}
    
        message = "Shell command executed successfully"
        return {"code": 200, "msg": message, "stdout": output}
    
    if __name__ == "__main__":
        command = run_shell_command(["ls", "-lah"], True)
        print(command)