如何将Python子进程的stderr和stdout重定向到多个文件?
如何将Python子进程的stderr和stdout重定向到多个文件?
我只想将stderr和stdout重定向到多个文件。
例如:
stderr应该重定向到file_1和file_2。
我正在使用以下代码将输出重定向到单个文件。
subprocess.Popen("my_commands",shell=True,stdout=log_file,stderr=err_file,executable="/bin/bash")
上述代码将stdout和stderr重定向到单个文件。
有谁能告诉我如何同时将输出重定向到log_file和err_file(例如,stdout应重定向到log_file和err_file,stderr应重定向到err_file和new_file)的方法吗?
问题的出现的原因是希望将Python子进程的标准输出和标准错误输出分别重定向到多个文件中,但是由于Popen只能接受操作系统文件描述符作为参数,因此无法直接使用类似于MultiOut的文件类来实现重定向。
解决方法是使用Python 3的asyncio特性,通过创建子进程并同时复制其标准输出和标准错误输出到指定的文件中。下面是一个使用asyncio的示例代码,它运行一个Bash脚本multitest.bsh,并将其标准输出重定向到q1和q2文件,将标准错误输出重定向到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文件中包含了标准输出的内容,q3文件中包含了标准错误输出的内容,q2文件中包含了标准输出和标准错误输出的内容。
需要注意的是,数据被写入文件的时间取决于调度的协程,具体的时间取决于当前系统的负载情况。为了保持标准输出和标准错误输出的行同步,代码中添加了`sleep 0.01`命令来延迟输出,没有这个延迟的话,q2文件中的标准输出和标准错误输出可能不会很好地交替出现。还有其他更好的方法来实现同步,但目前我对asyncio编程还不是很熟悉。
最后,如果希望使用subprocess.Popen来实现相同的功能,可以参考更新后的回答。