我的python脚本需要调用一个程序,检测它是否失败(例如,result != 0
)并将该程序的输出发送到stdout,就像正常加上日志文件一样。
我的默认shell是bash。我使用的是Python 2.7.9
要将输出发送到stdout和文件,我通常会使用tee
:
result = subprocess.call('some_program --an-option | tee -a ' + logfile , shell=True)
但是,即使第一个命令失败,bash中的管道也会返回true,因此这种方法无法检测命令是否失败。
如果我尝试在命令中使用set -o pipefail
(这样结果将指示第一个命令是否失败),如下所示:
result = subprocess.call('set -o pipefail && some_program --an_option | tee -a ' + logfile , shell=True)
我收到错误/bin/sh: 1: set: Illegal option -o pipefail
python中是否有办法调用命令,将输出发送到普通的stdout控制台和日志文件,仍检测命令是否失败?
注意:我们必须继续向stdout发送some_program的输出,因为stdout正被发送到websocket。
答案 0 :(得分:1)
我收到错误/ bin / sh:1:set:Illegal option -o pipefail
通过executable='/bin/bash'
,否则使用/bin/sh
。
您可以在纯Python中实现tee
:
#!/usr/bin/env python2
import sys
from subprocess import Popen, PIPE
chunk_size = 1 << 13
p = Popen(["some_program", "--an-option"], stdout=PIPE, bufsize=1)
with p.stdout, open('logfile', 'ab') as logfile:
for chunk in iter(lambda: p.stdout.read(chunk_size), b''):
sys.stdout.write(chunk)
logfile.write(chunk)
if p.wait() != 0:
raise Error
答案 1 :(得分:0)
我倾向于将stdout发送到管道,然后在Python代码中读取管道。 Python代码可以根据需要写入stdout,文件等。它还可以让你设置shell = False,因为将其设置为True是一个潜在的安全问题,如文档中所述。
答案 2 :(得分:0)
但是,即使第一个命令,bash中的管道也会返回true 失败,因此这种方法无法检测命令是否失败。
事实并非如此
但我认为你的意思是:'some_program --an-option | tee -a ' + logfile
退出状态代码总是为0,即使在任何命令部分都失败了。
好吧,使用多个命令(使用&&
或||
时)或通过管道连接多个命令会导致返回时出现不可靠的退出状态代码。
无论如何,在命令中:some_program --an-option | tee -a ' + logfile
如果some_program失败,则不会写入logfile。所以你不必担心退出代码。
无论如何,与子流程一起管道的最佳方法是创建Popen个对象和处理stdout
和stdin
:
将子进程导入为sp
STATUS_OK = 0
logfile = '/tmp/test.log'
commands = {
'main' : 'ls /home',
'pipe_to': 'tee -a ' + logfile
}
process = sp.Popen(commands['main'], shell=True, stdout=sp.PIPE)
# explicitly force waits till command terminate, set and return exit status code
process.wait()
if process.returncode == STATUS_OK:
stdoutdata = process.communicate()[0]
# pipe last command output to "tee" command
sp.Popen(commands['pipe_to'], stdin=sp.PIPE, shell=1).communicate(stdoutdata)
else:
# do something when command fails 'ls /hom' (in this case) fails
pass
就是这样!
我是最后一个Popen,我们调用Popen.communicate()将ls
命令的最后一个输出发送到tee
命令STDIN。
在Python文档中有一个名为Replacing shell pipeline的小教程,也许你想看看。