从子进程运行的子进程的加扰输出

时间:2011-02-28 08:48:37

标签: python subprocess

我正在使用以下代码运行另一个python脚本。我面临的问题是该脚本的输出是以无序的方式出现的。 从命令行运行它时,我得到正确的输出,即:

  

这里的一些输出
  编辑xml文件并保存更改
  上传xml文件..

使用subprocess运行脚本时,我会以相反的顺序获取一些输出:

  

正确输出到此处
  上传xml文件..
  编辑xml文件并保存更改

脚本执行时没有错误并进行了正确的更改。所以我认为罪魁祸首可能是调用子脚本的代码,但我找不到问题:

    cmd = "child_script.py"
    proc = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
    (fout ,ferr) = ( proc.stdout, proc.stderr )
    print "Going inside while - loop"
    while True:
        line = proc.stdout.readline()
        print line
        fo.write(line)
        try : 
            err = ferr.readline()
            fe.write(err)
        except Exception, e:
            pass
        if not line:
            pass
            break

[编辑]:fo和fe是输出和错误日志的文件句柄。此脚本也在Windows.Sorry上运行,因为它缺少这些细节。

2 个答案:

答案 0 :(得分:4)

你引用的剧本部分存在一些问题,我担心:

  • 正如detly的评论中所述,fofe是什么?据推测,那些是您正在编写子进程输出的对象? (更新:您表明这些都是用于编写输出日志。)
  • 第3行有一个缩进错误。(更新:我已在原帖中修复了这个问题。)
  • 您指定stderr=subprocess.STDOUT,因此:(a)ferr在您的循环中始终为None;(b)由于缓冲,标准输出和错误可能以不可预测的方式混合。但是,它从您的代码中看起来好像您实际上想要单独处理标准输出和标准错误,因此可能请尝试使用stderr=subprocess.PIPE

jsbueno建议重写你的循环是个好主意:

from subprocess import Popen, PIPE
proc = Popen(["child_script.py"], stdout=PIPE, stderr=PIPE)
fout, ferr = proc.stdout, proc.stderr
for line in fout:
    print(line.rstrip())
    fo.write(line)
for line in ferr:
    fe.write(line)

...或者进一步减少它,因为看起来目的本质上是你只想将子进程的标准输出和标准错误写入fofe,只是做:

proc = subprocess.Popen(["child_script.py"], stdout=fo, stderr=fe)

如果您仍然看到fo正在写入的文件中交换的输出行,那么我们只能假设在子脚本中有某种方式可以发生这种情况。例如子脚本多线程?是否通过另一个函数的回调打印了一行?

答案 1 :(得分:2)

大多数时候我看到输出顺序因执行而异,有些输出被发送到C标准IO流stdin,有些输出被发送到stderr。 stdout和stderr的缓冲特性取决于它们是否连接到终端,管道,文件等:

NOTES
   The stream stderr is unbuffered.  The stream stdout is
   line-buffered when it points to a terminal.  Partial lines
   will not appear until fflush(3) or exit(3) is called, or a
   newline is printed.  This can produce unexpected results,
   especially with debugging output.  The buffering mode of
   the standard streams (or any other stream) can be changed
   using the setbuf(3) or setvbuf(3) call.  Note that in case
   stdin is associated with a terminal, there may also be
   input buffering in the terminal driver, entirely unrelated
   to stdio buffering.  (Indeed, normally terminal input is
   line buffered in the kernel.)  This kernel input handling
   can be modified using calls like tcsetattr(3); see also
   stty(1), and termios(3).

所以也许您应该将stdout和stderr配置为转到相同的源,因此相同的缓冲将应用于两个流。

此外,有些程序直接打开终端open("/dev/tty",...)(主要是因为他们可以读取密码),所以将终端输出与管道输出进行比较并不总是有效。

此外,如果您的程序将直接write(2)调用与标准IO调用混合,则输出顺序可能会根据不同的缓冲选项而有所不同。

我希望其中一个是正确的:)让我知道哪个,如果有的话。