Python中是否有办法完成UNIX命令行tee
的等效操作?我正在做一个典型的fork / exec模式,我希望来自子节点的stdout同时出现在日志文件和父节点的stdout上,而不需要任何缓冲。
例如,在这个python代码中,子节点的stdout最终出现在日志文件中,但不在父节点的stdout中。
pid = os.fork()
logFile = open(path,"w")
if pid == 0:
os.dup2(logFile.fileno(),1)
os.execv(cmd)
编辑:我不想使用子进程模块。我正在对子进程做一些复杂的事情,需要我手动调用fork
。
答案 0 :(得分:6)
在下文中,SOMEPATH
是子可执行文件的路径,格式适合subprocess.Popen
(请参阅其文档)。
import sys, subprocess
f = open('logfile.txt', 'w')
proc = subprocess.Popen(SOMEPATH, stdout=subprocess.PIPE)
while True:
out = proc.stdout.read(1)
if out == '' and proc.poll() != None:
break
if out != '':
# CR workaround since chars are read one by one, and Windows interprets
# both CR and LF as end of lines. Linux only has LF
if out != '\r': f.write(out)
sys.stdout.write(out)
sys.stdout.flush()
答案 1 :(得分:6)
这里有一个不使用subprocess
模块的工作解决方案。虽然,您可以将它用于tee进程,同时仍然使用exec*
函数套件进行自定义子进程(只需使用stdin=subprocess.PIPE
然后将描述符复制到stdout)。
import os, time, sys
pr, pw = os.pipe()
pid = os.fork()
if pid == 0:
os.close(pw)
os.dup2(pr, sys.stdin.fileno())
os.close(pr)
os.execv('/usr/bin/tee', ['tee', 'log.txt'])
else:
os.close(pr)
os.dup2(pw, sys.stdout.fileno())
os.close(pw)
pid2 = os.fork()
if pid2 == 0:
# Replace with your custom process call
os.execv('/usr/bin/yes', ['yes'])
else:
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
pass
请注意,tee
命令在内部执行与Ben在其答案中建议的相同:读取输入并在写入时循环输出文件描述符。它可能更有效,因为优化的实现,因为它是用C编写的,但是你有不同管道的开销(不知道哪个解决方案更有效,但在我看来,重新分配自定义文件对象stdout
是一个更优雅的解决方案。)
更多资源:
答案 2 :(得分:2)
像这样的方法会做你想要的吗?
import sys
class Log(object):
def __init__(self, filename, mode, buffering):
self.filename = filename
self.mode = mode
self.handle = open(filename, mode, buffering)
def write(self, thing):
self.handle.write(thing)
sys.stdout.write(thing)
您可能需要为此实现更多file
界面才能真正有用(如果您需要,我已经遗漏了mode
和buffering
正确的默认值)。然后,您可以在子进程中将所有写入操作都写入Log实例。或者,如果你想成为一个神奇的人,并且你确定你实现了足够的file
接口,事情不会摔倒并死亡,你可能会分配{{1成为这个类的一个实例。然后我想任何写入stdout的方法,包括sys.stdout
,都将通过日志类。
编辑添加:显然,如果您分配给print
,则必须在sys.stdout
方法中执行其他操作以将输出回显到标准输出!我认为您可以使用write
。
答案 3 :(得分:2)
哦,你。在看到你的例子的最后一行之前,我得到了一个不错的答案:execv()。好吧,大便。最初的想法是用这个博客帖子的tee类的实例替换每个子进程'stdout,并将流拆分为原始stdout和日志文件:
http://www.shallowsky.com/blog/programming/python-tee.html
但是,因为你正在使用execv(),所以子进程'tee实例会被破坏,所以这不起作用。
不幸的是,对于您而言,我找不到任何“开箱即用”的问题解决方案。最接近的是在子流程中生成实际的T恤程序;如果你想要更加跨平台,你可以派一个简单的Python代替。
首先要知道编写T恤替代品时:tee确实是一个简单的程序。在我见过的所有真正的C实现中,它并不比这复杂得多:
while((character = read()) != EOF) {
/* Write to all of the output streams in here, then write to stdout. */
}
不幸的是,你不能只将两个流连接在一起。这将是非常有用的(因此一个流的输入将自动转发出另一个流),但我们没有这样的奢侈,没有自己编码。所以,Eli和我将有非常相似的答案。不同的是,在我的回答中,Python'tee'将通过管道在一个单独的进程中运行;那样,父线程仍然有用!
(请记住:复制博客文章的开球课程。)
import os, sys
# Open it for writing in binary mode.
logFile=open("bar", "bw")
# Verbose names, but I wanted to get the point across.
# These are file descriptors, i.e. integers.
parentSideOfPipe, childSideOfPipe = os.pipe()
# 'Tee' subprocess.
pid = os.fork()
if pid == 0:
while True:
char = os.read(parentSideOfPipe, 1)
logFile.write(char)
os.write(1, char)
# Actual command
pid = os.fork()
if pid == 0:
os.dup2(childSideOfPipe, 1)
os.execv(cmd)
如果那不是您想要的,我很抱歉,但这是我能找到的最佳解决方案。
祝你项目的其余部分好运!
答案 4 :(得分:0)
第一个明显的答案是分叉实际的T恤过程,但这可能并不理想。
tee代码(来自coreutils)只是读取每一行并依次写入每个文件(有效缓冲)。