如何在Python中使用子进程重定向输出?


96

我在命令行中执行的操作:

cat file1 file2 file3 > myfile

我想用python做什么:

import subprocess, shlex
my_cmd = 'cat file1 file2 file3 > myfile'
args = shlex.split(my_cmd)
subprocess.call(args) # spits the output in the window i call my python program

在子进程中执行这样的命令不会给您任何输出。也许您想在不使用> myfilecat file1 file2 file3的输出重定向到python的情况下运行它?
PoltoS 2011年

@PoltoS我想加入一些文件,然后处理生成的文件。我以为使用猫是最简单的选择。有更好的/ pythonic的方法吗?
catatemypythoncode

os.sendfile()基于解决方案的方法是可能的,请参阅在python中重现unix cat命令
jfs 2015年

1
我认为输出重定向('>'或'>>')在子进程中不起作用.Popen(至少在Python 2.7中)(在shell = True模式下)在此示例中,正如其他人指出的那样,您可以解决通过不使用重定向,但是在其他情况下重定向很有用。如果子流程中不支持重定向或管道。应记录Popen(和/或在修复此问题之前不建议使用os.system())
Ribo

Answers:


19

更新:不鼓励使用os.system,尽管在Python 3中仍然可用。


用途os.system

os.system(my_cmd)

如果您确实要使用子流程,请使用以下解决方案(大部分从子流程的文档中删除):

p = subprocess.Popen(my_cmd, shell=True)
os.waitpid(p.pid, 0)

OTOH,您可以完全避免系统调用:

import shutil

with open('myfile', 'w') as outfile:
    for infile in ('file1', 'file2', 'file3'):
        shutil.copyfileobj(open(infile), outfile)

1
它可以工作,但是让我再问您:如果os.system已经完成工作,那么子流程库有什么意义呢?我感觉应该改用子进程,因为它是专用于此任务的库,尽管由于我是为我自己而做的,所以这次我可以使用os.system了。
catatemypythoncode

子流程库比灵活得多os.system,并且可以os.system精确建模,但使用起来也更加复杂。
Marcelo Cantos

13
os.system来过subprocess。前者是遗留API,后者打算替换。
圣诞老人

5
@catatemypythoncode:您不应使用os.system()shell=True。要重定向子流程的输出,请使用Ryan Thompson的答案中stdout所示的参数。尽管您不需要子进程(),但可以使用纯Python串联文件。cat
jfs 2015年

4
OTOH =另一方面
Cephlin

271

Python 3.5+中,要重定向输出,只需将参数的打开文件句柄传递stdoutsubprocess.run

# Use a list of args instead of a string
input_files = ['file1', 'file2', 'file3']
my_cmd = ['cat'] + input_files
with open('myfile', "w") as outfile:
    subprocess.run(my_cmd, stdout=outfile)

正如其他人指出的那样,cat为此完全不需要外部命令。


9
这应该是使用Python外壳程序时管道的一般问题的答案
Kaushik Ghose 2014年

46
这是正确的答案,而不是标记为正确的答案。
贾斯汀·布莱克2014年

7
对于subprocess.run(my_cmd, stdout=outfile)要替换的Python 3.5+,使用subprocess.call(...)
奥斯汀·耶茨

1
值得注意的是,这不符合自定义文件对象的工作,如果他们没有的fileno场(如果他们是不是真正的文件。)
埃利泽米伦

1
由于Python <3.5现已被弃用,因此我用您的评论@AustinYates更新了答案。
Greg Dubicki

5

@PoltoS我想加入一些文件,然后处理生成的文件。我以为使用猫是最简单的选择。有更好的/ pythonic的方法吗?

当然:

with open('myfile', 'w') as outfile:
    for infilename in ['file1', 'file2', 'file3']:
        with open(infilename) as infile:
            outfile.write(infile.read())

1
size = 'ffprobe -v error -show_entries format=size -of default=noprint_wrappers=1:nokey=1 dump.mp4 > file'
proc = subprocess.Popen(shlex.split(size), shell=True)
time.sleep(1)
proc.terminate() #proc.kill() modify it by a suggestion
size = ""
with open('file', 'r') as infile:
    for line in infile.readlines():
        size += line.strip()

print(size)
os.remove('file')

当您使用进程时,必须终止该进程。这是一个示例。如果不终止该进程,则文件将为空,并且无法读取任何内容。它可以在Windows上运行。我无法确保它可以在Unix上运行。


1
这是一个糟糕的代码示例(它不会在Unix上运行;它表明陋习for line in .readlines():s +=),并proc.kill()可能导致一般信息丢失(它不允许子进程正常终止(在UNIX上) -未刷新的内容丢失)。无论如何,关于缓冲的注释更适合作为注释。
jfs 2015年

我在Windows上运行它是可以的(因为kill等于在Windows上终止)。在Unix上,您应该使用proc.terminate()。@ JF Sebastian我的计算机上没有Unix系统。
wyx

如果您使用的是Windows然后放下shlex.split(),降shell=True,降>file,降open(),等和使用stdout=PIPETimer(1, proc.terminate).start(); output = proc.communicate()[0]代替。这是完整的示例。更多解决方案:停止在Python中读取进程输出而不会挂起?注意:问题中没有要求您需要手动终止子进程的情况-您可以解决其他问题,例如,如果某个进程的stdout是tty但它不在主题范围内,则该进程的行为可能会有所不同。
jfs 2015年

0

一种有趣的情况是通过将类似文件附加到文件来更新文件。这样就不必在此过程中创建一个新文件。在需要附加大文件的情况下,此功能特别有用。这是直接从python使用终端命令行的一种可能性。

import subprocess32 as sub

with open("A.csv","a") as f:
    f.flush()
    sub.Popen(["cat","temp.csv"],stdout=f)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.