我正在寻找一种Python解决方案,它允许我将命令的输出保存在文件中,而不会将其从控制台中隐藏。
仅供参考:我问的是tee(作为Unix命令行实用程序),而不是Python intertools模块中具有相同名称的函数。
tee
,在Windows下不可用)stderr
和stdout
stderr = subprocess.STDOUT
将无效。到目前为止,我找到了一些不完整的解决方案:
Diagram http://blog.i18n.ro/wp-content/uploads/2010/06/Drawing_tee_py.png
#!/usr/bin/python
from __future__ import print_function
import sys, os, time, subprocess, io, threading
cmd = "python -E test_output.py"
from threading import Thread
class StreamThread ( Thread ):
def __init__(self, buffer):
Thread.__init__(self)
self.buffer = buffer
def run ( self ):
while 1:
line = self.buffer.readline()
print(line,end="")
sys.stdout.flush()
if line == '':
break
proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdoutThread = StreamThread(io.TextIOWrapper(proc.stdout))
stderrThread = StreamThread(io.TextIOWrapper(proc.stderr))
stdoutThread.start()
stderrThread.start()
proc.communicate()
stdoutThread.join()
stderrThread.join()
print("--done--")
#### test_output.py ####
#!/usr/bin/python
from __future__ import print_function
import sys, os, time
for i in range(0, 10):
if i%2:
print("stderr %s" % i, file=sys.stderr)
else:
print("stdout %s" % i, file=sys.stdout)
time.sleep(0.1)
实际输出
stderr 1
stdout 0
stderr 3
stdout 2
stderr 5
stdout 4
stderr 7
stdout 6
stderr 9
stdout 8
--done--
预期产量是订购的。备注,不允许修改Popen只使用一个PIPE,因为在现实生活中我会想要用stderr和stdout做不同的事情。
即使在第二种情况下,我也无法像实时一样获得实时,实际上所有结果都是在完成过程时收到的。默认情况下,Popen应该不使用缓冲区(bufsize = 0)。
答案 0 :(得分:16)
我发现这是一个相当古老的帖子,但万一有人还在寻找一种方法:
proc = subprocess.Popen(["ping", "localhost"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
with open("logfile.txt", "w") as log_file:
while proc.poll() is None:
line = proc.stderr.readline()
if line:
print "err: " + line.strip()
log_file.write(line)
line = proc.stdout.readline()
if line:
print "out: " + line.strip()
log_file.write(line)
答案 1 :(得分:13)
最后,我必须自己在Python中实现tee()
命令。
您可以从http://github.com/pycontribs/tendo/blob/master/tendo/tee.py
获取此信息目前,它确实允许您执行以下操作:
tee("python --v") # works just like os.system()
tee("python --v", "log.txt") # file names
tee("python --v", file_handle)
import logging
tee("python --v", logging.info) # receive a method
目前唯一的限制是它无法区分stderr
和stdout
,这意味着它会合并它们。
答案 2 :(得分:7)
这是Python的tee
的简单端口。
import sys
sinks = sys.argv[1:]
sinks = [open(sink, "w") for sink in sinks]
sinks.append(sys.stderr)
while True:
input = sys.stdin.read(1024)
if input:
for sink in sinks:
sink.write(input)
else:
break
我现在正在Linux上运行,但这应该适用于大多数平台。
现在对于subprocess
部分,我不知道您希望如何将子流程的stdin
,stdout
和stderr
“连接”到您的stdin
},stdout
,stderr
和文件汇,但我知道你可以这样做:
import subprocess
callee = subprocess.Popen( ["python", "-i"],
stdin = subprocess.PIPE,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE
)
现在,您可以像普通文件一样访问callee.stdin
,callee.stdout
和callee.stderr
,从而启用上述“解决方案”。如果您想获得callee.returncode
,则需要额外拨打callee.poll()
。
小心写入callee.stdin
:如果进程已退出,则可能会出现错误(在Linux上,我得到IOError: [Errno 32] Broken pipe
)。
答案 3 :(得分:3)
如果您不想与流程进行交互,可以使用子流程模块。
示例:
tester.py
import os
import sys
for file in os.listdir('.'):
print file
sys.stderr.write("Oh noes, a shrubbery!")
sys.stderr.flush()
sys.stderr.close()
testing.py
import subprocess
p = subprocess.Popen(['python', 'tester.py'], stdout=subprocess.PIPE,
stdin=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
print stdout, stderr
在您的情况下,您可以先将stdout / stderr写入文件。您也可以通过通信向您的流程发送参数,但我无法弄清楚如何不断地与子流程进行交互。
答案 4 :(得分:3)
python中有一些与subprocess.PIPE相关的细微问题/错误: http://bugs.python.org/issue1652
显然这是在python3 +中修复的,但在python 2.7及更早版本中没有。为此您需要使用: code.google.com/p/python-subprocess32 /
答案 5 :(得分:0)
试试这个:
import sys
class tee-function :
def __init__(self, _var1, _var2) :
self.var1 = _var1
self.var2 = _var2
def __del__(self) :
if self.var1 != sys.stdout and self.var1 != sys.stderr :
self.var1.close()
if self.var2 != sys.stdout and self.var2 != sys.stderr :
self.var2.close()
def write(self, text) :
self.var1.write(text)
self.var2.write(text)
def flush(self) :
self.var1.flush()
self.var2.flush()
stderrsav = sys.stderr
out = open(log, "w")
sys.stderr = tee-function(stderrsav, out)
答案 6 :(得分:0)
我写了一篇用Python包装shell命令的东西。
主要优势:
主要缺点:
答案 7 :(得分:0)
我的解决方案并不优雅,但可以。
您可以使用Powershell来访问WinOS下的“ tee”。
import subprocess
import sys
cmd = ['powershell', 'ping', 'google.com', '|', 'tee', '-a', 'log.txt']
if 'darwin' in sys.platform:
cmd.remove('powershell')
p = subprocess.Popen(cmd)
p.wait()
答案 8 :(得分:0)
这是可以做到的
import sys
from subprocess import Popen, PIPE
with open('log.log', 'w') as log:
proc = Popen(["ping", "google.com"], stdout=PIPE, encoding='utf-8')
while proc.poll() is None:
text = proc.stdout.readline()
log.write(text)
sys.stdout.write(text)
答案 9 :(得分:0)
如果不需要python 3.6成为问题,现在可以使用asyncio做到这一点。此方法使您可以分别捕获stdout和stderr,但仍然可以不使用线程而将两者都流到tty。这是一个粗略的轮廓:
class RunOutput():
def __init__(self, returncode, stdout, stderr):
self.returncode = returncode
self.stdout = stdout
self.stderr = stderr
async def _read_stream(stream, callback):
while True:
line = await stream.readline()
if line:
callback(line)
else:
break
async def _stream_subprocess(cmd, stdin=None, quiet=False, echo=False) -> RunOutput:
if isWindows():
platform_settings = {'env': os.environ}
else:
platform_settings = {'executable': '/bin/bash'}
if echo:
print(cmd)
p = await asyncio.create_subprocess_shell(cmd,
stdin=stdin,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
**platform_settings)
out = []
err = []
def tee(line, sink, pipe, label=""):
line = line.decode('utf-8').rstrip()
sink.append(line)
if not quiet:
print(label, line, file=pipe)
await asyncio.wait([
_read_stream(p.stdout, lambda l: tee(l, out, sys.stdout)),
_read_stream(p.stderr, lambda l: tee(l, err, sys.stderr, label="ERR:")),
])
return RunOutput(await p.wait(), out, err)
def run(cmd, stdin=None, quiet=False, echo=False) -> Result:
loop = asyncio.get_event_loop()
result = loop.run_until_complete(
_stream_subprocess(cmd, stdin=stdin, quiet=quiet, echo=echo)
)
return result
上面的代码基于此博客文章:https://kevinmccarthy.org/2016/07/25/streaming-subprocess-stdin-and-stdout-with-asyncio-in-python/