从子进程读取的实时标准输出仅在从 PyCharm 而不是终端运行时才有效

Posted

技术标签:

【中文标题】从子进程读取的实时标准输出仅在从 PyCharm 而不是终端运行时才有效【英文标题】:Real-time stdout read from subprocess works only when running from PyCharm and not Terminal 【发布时间】:2017-09-24 01:20:25 【问题描述】:

应用程序的作用:运行子进程并将标准输出实时显示到 Tkinter 文本框小部件。

当我从 PyCharm 运行应用程序时,这非常有效。

当我从终端 ./application.py 运行应用程序时,它不会实时显示,而是会在进程完成后全部显示。


一些细节:

我有一个子进程正在运行(子进程每 1 秒打印出“SPAM”,持续 10 秒):

process = subprocess.Popen(<some file path>, stdout=subprocess.PIPE, universal_newlines=True)

我正在将标准输出打印到 Tkinter 文本框小部件:

for stdout_line in iter(process.stdout.readline, ""):
     self.app.pm_textbox.see(tk.END)
     self.app.pm_textbox.insert(tk.INSERT, stdout_line)

所以我的问题是什么可能导致从终端和 PyCharm 运行以不同方式显示标准输出数据?

【问题讨论】:

它继承了当前的stdinstderr。可能在 PyCharm 中这些是管道。不过,我不确定。我认为 PyCharm 对调试有很大的帮助,我没有安装它来测试它的作用。从交互式 shell 运行时,stdinstderr 应该用于终端。尝试将它们覆盖为subprocess.DEVNULL,这样它们在两种情况下都是相同的。 @eryksun 很遗憾,这并没有解决问题。 您是在 Linux 或其他具有 stdbuf 命令的类 Unix 操作系统上开发吗?如果是这样,请尝试将命令更改为 ['stdbuf', '-oL', filepath] 以覆盖默认行缓冲。如果可行,我想知道 PyCharm 在做什么。 @eryksun 我正在 Ubuntu 16.04 上开发。添加此命令似乎没有改变任何东西 - PyCharm 仍在工作,从终端启动应用程序没有。 这里的症状看起来像是在将输出写入管道之前完全缓冲了输出。典型的缓冲区大小是 4 KiB 或 8 KiB,所以如果你没有那么多数据,缓冲区只会在程序退出时刷新到管道。 stdbuf 并不总是有效。它只会改变默认行为,但有些程序会覆盖它。 【参考方案1】:

我知道这是一个旧线程,但仍然 -

我遇到了和你一样的问题。我问了一个question,这似乎有助于我在处理实时输出方面找到正确的方向。我的问题是我需要区分由 CR 产生的输出并在我的程序中模拟该行为。但后来我也遇到了你的问题——我工作了 3 天,试图找到一个解决方案来解决为什么会发生这种情况。

在阅读了this 并且对他们在那里所做的事情没有运气之后,我发现这个帖子没有答案,但一个comment 经过一些修改后终于帮助了我。

最后对我有用的是:

#                        This seems to handle    This seems to handle
#                        the terminal output     the PyCharm output
proc = subprocess.Popen(["stdbuf", "-oL"] + cmd, stdout=subprocess.PIPE)

out = io.open(proc.stdout.fileno(), mode='r', encoding="utf-8", newline='')
should_cr = False
for line in out:
    if should_cr is True:
        sys.stdout.write("\r")
        sys.stdout.flush()

    if line.endswith(os.linesep):
        should_cr = False
        sys.stdout.write(line)
        sys.stdout.flush()

    elif line.endswith("\r"):
        should_cr = True
        line = line.rstrip()
        sys.stdout.write(line)
        sys.stdout.flush()

如果你不考虑 CR 输出,那当然更简单:

#                        This seems to handle    This seems to handle
#                        the terminal output     the PyCharm output
proc = subprocess.Popen(["stdbuf", "-oL"] + cmd, stdout=subprocess.PIPE)
for line in iter(proc.stdout.readline, ''):
    print(line)

请注意,这在 Python 2.7 中适用于我

【讨论】:

以上是关于从子进程读取的实时标准输出仅在从 PyCharm 而不是终端运行时才有效的主要内容,如果未能解决你的问题,请参考以下文章

从子进程中实时捕获标准输出

Jupyter notebook 中 Python 子进程的实时标准输出输出

Python:如何写入子进程的标准输入并实时读取其输出

如何在 node.js 子进程模块中将消息和标准输出从子进程传递给父进程?

Python分别从子进程stdout和stderr读取,同时保留顺序

如何在从我产生的工作中获取一行标准输出时阻止 bash