【问题标题】:How to redirect python subprocess stderr and stdout to multiple files? [duplicate]如何将python子进程stderr和stdout重定向到多个文件? [复制]
【发布时间】:2017-05-08 02:32:22
【问题描述】:

我只想将 stderr 和 stdout 重定向到多个文件。 例如: stderr 应该重定向到 file_1 和 file_2。

我正在使用下面将输出重定向到单个文件。

subprocess.Popen("my_commands",shell=True,stdout=log_file,stderr=err_file,executable="/bin/bash")

以上内容将stdoutstderr 重定向到单个文件。
任何人都可以告诉如何做同样的事情(重定向输出到两个文件 log_file 和 err_file 例如stdout 应该重定向到 log_file 和 err_file 和 stderr 应该重定向到 err_file 和 new_file)

【问题讨论】:

  • 您可能会在这里找到一些有用的答案:stackoverflow.com/questions/2996887/…
  • 你告诉我的和我用的一样。但它应该重定向到多个文件描述符。不仅是单个文件。
  • 没用。不工作
  • 使用我回答中的代码,您可以重定向到尽可能多的文件。 :)
  • 您使用的是哪个 Python 版本?在将输出数据写入文件之前,您可以等待“my_commands”完成吗?或者您是否需要在“my_commands”仍在运行时写入文件?如果你可以等待,你想要的很容易。如果您需要在“my_commands”仍在运行时写入 3 个文件,那就有点棘手了。

标签: python redirect


【解决方案1】:

您可以创建自己的类似文件的类来写入多个文件句柄。这是一个简单的示例,其中包含重定向 sys.stdoutsys.stderr 的测试。

import sys

class MultiOut(object):
    def __init__(self, *args):
        self.handles = args

    def write(self, s):
        for f in self.handles:
            f.write(s)

with open('q1', 'w') as f1, open('q2', 'w') as f2, open('q3', 'w') as f3:
    sys.stdout = MultiOut(f1, f2)
    sys.stderr = MultiOut(f3, f2)
    for i, c in enumerate('abcde'):
        print(c, 'out')
        print(i, 'err', file=sys.stderr)

运行该代码后,这些文件包含以下内容:

q1

a out
b out
c out
d out
e out    

第三季度

0 err
1 err
2 err
3 err
4 err    

q2

a out
0 err
b out
1 err
c out
2 err
d out
3 err
e out
4 err

FWIW,如果你愿意,你甚至可以这样做:

sys.stdout = MultiOut(f1, f2, sys.stdout)
sys.stderr = MultiOut(f3, f2, sys.stderr)

不幸的是,像MultiOut 这样的类文件对象不能与Popen 一起使用,因为Popen 通过底层操作系统文件描述符访问文件,即它想要操作系统认为是文件的东西,所以只有提供有效fileno 方法的Python 对象才能用于Popen 的文件参数。

相反,我们可以使用 Python 3 的 asyncio 功能来执行 shell 命令并同时复制其 stdout 和 stderr 输出。

首先,这是我用来测试以下 Python 代码的简单 Bash 脚本。它只是循环一个数组,将数组内容回显到 stdout,将数组索引回显到 stderr,就像前面的 Python 示例一样。

multitest.bsh

#!/usr/bin/env bash

a=(a b c d e)
for((i=0; i<${#a[@]}; i++))
do 
    echo "OUT: ${a[i]}"
    echo "ERR: $i" >&2
    sleep 0.01
done

输出

OUT: a
ERR: 0
OUT: b
ERR: 1
OUT: c
ERR: 2
OUT: d
ERR: 3
OUT: e
ERR: 4

这是运行 multitest.bsh 的 Python 3 代码,将其 stdout 输出通过管道传输到文件 q1 和 q2,并将其 stderr 输出通过管道传输到 q3 和 q2。

import asyncio
from asyncio.subprocess import PIPE

class MultiOut(object):
    def __init__(self, *args):
        self.handles = args

    def write(self, s):
        for f in self.handles:
            f.write(s)

    def close(self):
        pass

@asyncio.coroutine
def copy_stream(stream, outfile):
    """ Read from stream line by line until EOF, copying it to outfile. """
    while True:
        line = yield from stream.readline()
        if not line:
            break
        outfile.write(line) # assume it doesn't block

@asyncio.coroutine
def run_and_pipe(cmd, fout, ferr):
    # start process
    process = yield from asyncio.create_subprocess_shell(cmd,
        stdout=PIPE, stderr=PIPE, executable="/bin/bash")

    # read child's stdout/stderr concurrently
    try:
        yield from asyncio.gather(
            copy_stream(process.stdout, fout),
            copy_stream(process.stderr, ferr))
    except Exception:
        process.kill()
        raise
    finally:
        # wait for the process to exit
        rc = yield from process.wait()
    return rc

# run the event loop
loop = asyncio.get_event_loop()

with open('q1', 'wb') as f1, open('q2', 'wb') as f2, open('q3', 'wb') as f3:
    fout = MultiOut(f1, f2)
    ferr = MultiOut(f3, f2)
    rc = loop.run_until_complete(run_and_pipe("./multitest.bsh", fout, ferr))
loop.close()
print('Return code:', rc)    

运行代码后,这些文件包含以下内容:

q1

OUT: a
OUT: b
OUT: c
OUT: d
OUT: e

第三季度

ERR: 0
ERR: 1
ERR: 2
ERR: 3
ERR: 4

q2

OUT: a
ERR: 0
OUT: b
ERR: 1
OUT: c
ERR: 2
OUT: d
ERR: 3
OUT: e
ERR: 4

asyncio 代码从J.F. Sebastian's answer 提升到问题Subprocess.Popen: cloning stdout and stderr both to terminal and variables。谢谢,J.F!

请注意,当调度的协程可以使用数据时,会将数据写入文件;确切的何时发生取决于当前的系统负载。所以我将sleep 0.01 命令放在multitest.bsh 中,以保持stdout 和stderr 行的处理同步。如果没有这种延迟, q2 中的 stdout 和 stderr 行通常不会很好地交错。可能有更好的方法来实现这种同步,但我仍然是异步编程的新手。

【讨论】:

  • 这对我不起作用。您能否建议与 subprocess.Popen 相同。谢谢。
  • @Swapnil 抱歉!我刚刚了解到这不适用于Popen,因为Popen 通过底层操作系统文件访问文件,这就是您收到AttributeError: 'MultiOut' object has no attribute 'fileno' 错误的原因。你想要的可能的,但有点棘手,我目前正在研究各种解决方案。
  • @Swapnil 请查看我的更新答案。
猜你喜欢
  • 1970-01-01
  • 2014-07-22
  • 1970-01-01
  • 2012-07-14
  • 1970-01-01
  • 1970-01-01
  • 2011-12-28
  • 2011-11-23
  • 2019-04-12
相关资源
最近更新 更多