如何使用python subprocess.call,将stdout的副本发送到logfile,同时检测第一个命令的结果

时间:2016-01-17 00:14:05

标签: python bash subprocess

我的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。

3 个答案:

答案 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个对象和处理stdoutstdin

将子进程导入为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的小教程,也许你想看看。