【问题标题】:Live stdout output from Python subprocess in Jupyter notebookJupyter notebook 中 Python 子进程的实时标准输出输出
【发布时间】:2016-12-01 15:33:21
【问题描述】:

我正在使用 subprocess 从 Python (3.5.2) 脚本运行命令行程序,该脚本在 Jupyter 笔记本中运行。子进程需要很长时间才能运行,因此我希望将其标准输出实时打印到 Jupyter 笔记本的屏幕上。

我可以在从终端运行的普通 Python 脚本中做到这一点。我这样做是:

def run_command(cmd):
from subprocess import Popen, PIPE
import shlex

with Popen(shlex.split(cmd), stdout=PIPE, bufsize=1, universal_newlines=True) as p:
    for line in p.stdout:
        print(line, end='')
    exit_code = p.poll()
return exit_code

但是,当我在 Jupyter 笔记本中运行脚本时,它不会将标准输出实时打印到屏幕上。相反,它会在子进程完成运行后打印所有内容。

有人对如何解决这个问题有任何想法吗?

非常感谢, 强尼

【问题讨论】:

  • 尝试将 flush=True 添加到您的打印中
  • 谢谢,帕德莱克。我试过了,但是没有用。我还尝试在 for 循环之前添加 sys.stdout.flush() ,但也没有用。
  • 你在什么系统和版本的 Jupyter 上运行这个?我在 Ubuntu 和 Python 3 下使用 jupyter_client 4.3.0、jupyter_console 5.0.0 和 jupyter_core 4.1.0 运行您的代码,并在生成它的过程中逐步打印标准输出。
  • 你在运行什么命令?
  • @jonnat,谢谢!我的版本是:jupyter==1.0.0 jupyter-client==4.3.0 jupyter-console==5.0.0 jupyter-core==4.1.0

标签: python subprocess ipython stdout jupyter


【解决方案1】:

ipython notebook 有它自己的support for running shell commands。如果您不需要捕获子流程的东西,您可以这样做

cmd = 'ls -l'
!{cmd}

使用 ! 执行的命令的输出会自动通过笔记本传输。

【讨论】:

  • 优秀的答案。如果要捕获命令的输出,可以使用output = !{cmd}
【解决方案2】:

如果您设置了stdout = None(这是默认设置,因此您可以完全省略stdout 参数),那么您的进程应该将其输出写入运行您的 IPython 笔记本服务器的终端。

这是因为默认行为是子进程从父文件处理程序继承(请参阅docs)。

您的代码如下所示:

from subprocess import Popen, PIPE
import shlex

def run_command(cmd):
    p = Popen(shlex.split(cmd), bufsize=1, universal_newlines=True)
    return p.poll()

这不会打印到浏览器中的笔记本,但至少您可以在其他代码运行时异步查看子进程的输出。

希望这会有所帮助。

【讨论】:

    【解决方案3】:

    Jupyter 与 stdout 和 stderr 混为一谈。这应该会得到你想要的,并在命令启动失败时给你一个更有用的异常。

    import signal
    import subprocess as sp
    
    
    class VerboseCalledProcessError(sp.CalledProcessError):
        def __str__(self):
            if self.returncode and self.returncode < 0:
                try:
                    msg = "Command '%s' died with %r." % (
                        self.cmd, signal.Signals(-self.returncode))
                except ValueError:
                    msg = "Command '%s' died with unknown signal %d." % (
                        self.cmd, -self.returncode)
            else:
                msg = "Command '%s' returned non-zero exit status %d." % (
                    self.cmd, self.returncode)
    
            return f'{msg}\n' \
                   f'Stdout:\n' \
                   f'{self.output}\n' \
                   f'Stderr:\n' \
                   f'{self.stderr}'
    
    
    def bash(cmd, print_stdout=True, print_stderr=True):
        proc = sp.Popen(cmd, stderr=sp.PIPE, stdout=sp.PIPE, shell=True, universal_newlines=True,
                        executable='/bin/bash')
    
        all_stdout = []
        all_stderr = []
        while proc.poll() is None:
            for stdout_line in proc.stdout:
                if stdout_line != '':
                    if print_stdout:
                        print(stdout_line, end='')
                    all_stdout.append(stdout_line)
            for stderr_line in proc.stderr:
                if stderr_line != '':
                    if print_stderr:
                        print(stderr_line, end='', file=sys.stderr)
                    all_stderr.append(stderr_line)
    
        stdout_text = ''.join(all_stdout)
        stderr_text = ''.join(all_stderr)
        if proc.wait() != 0:
            raise VerboseCalledProcessError(proc.returncode, cmd, stdout_text, stderr_text)
    

    【讨论】:

      【解决方案4】:

      用显式的 readline() 调用替换 for 循环对我有用。

      from subprocess import Popen, PIPE
      import shlex
      
      def run_command(cmd):
          with Popen(shlex.split(cmd), stdout=PIPE, bufsize=1, universal_newlines=True) as p:
              while True:
                  line = p.stdout.readline()
                  if not line:
                      break
                  print(line)    
              exit_code = p.poll()
          return exit_code
      

      即使在 4 年后,他们的迭代器仍然存在问题。

      【讨论】:

        猜你喜欢
        • 2019-10-01
        • 2021-03-03
        • 2011-08-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-09
        • 2013-06-29
        • 2016-02-15
        相关资源
        最近更新 更多