简体   繁体   English

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

[英]How use python subprocess.call, sending copy of stdout to logfile, while detecting result of first command

My python script needs to invoke a program, detect if it failed (eg, result != 0 ) and send the output of the program to both stdout like normal plus a log file. 我的python脚本需要调用程序,检测程序是否失败(例如, result != 0 ),并将程序的输出发送到stdout,就像普通的一样,并发送日志文件。

My default shell is bash. 我的默认外壳是bash。 I'm using Python 2.7.9 我正在使用Python 2.7.9

To send output to both stdout and a file I'd normally use tee : 要将输出发送到stdout和文件,我通常使用tee

result = subprocess.call('some_program --an-option  | tee -a ' + logfile , shell=True)

However, the pipe in bash will return true even if the first command fails, so this approach fails to detect if the command fails. 但是,即使第一个命令失败,bash中的管道也将返回true,因此该方法无法检测该命令是否失败。

If I try to use set -o pipefail in the command (so that the result will indicate if the first command fails) like this: 如果我尝试在命令中使用set -o pipefail (这样结果将指示第一个命令是否失败),如下所示:

result = subprocess.call('set -o pipefail && some_program --an_option  | tee -a ' + logfile , shell=True)

I get the error /bin/sh: 1: set: Illegal option -o pipefail 我收到错误/bin/sh: 1: set: Illegal option -o pipefail

Is there a way in python to invoke a command, send the output to both the normal stdout console and a logfile, and still detect if the command failed? python中是否有办法调用命令,将输出发送到普通stdout控制台和日志文件,并且仍然检测命令是否失败?

Note: we have to continue sending some_program's output to stdout since stdout is being sent to a websocket. 注意:由于将stdout发送到websocket,我们必须继续将some_program的输出发送到stdout。

I get the error /bin/sh: 1: set: Illegal option -o pipefail 我收到错误/ bin / sh:1:设置:非法选项-o pipefail

Pass executable='/bin/bash' otherwise /bin/sh is used. 传递executable='/bin/bash'否则使用/bin/sh

You could implement tee in pure Python: 您可以在纯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

My preference would to to send stdout to a pipe, and then read the pipe in the Python code. 我的偏好是将stdout发送到管道,然后在Python代码中读取管道。 The Python code can write to stdout, a file, etc as required. Python代码可以根据需要写入标准输出,文件等。 It would also enable you to set shell=False as setting it to True is a potential security issue, as mentioned in the documentation. 如文档中所述,它还使您可以将shell = False设置为True,因为将其设置为True是潜在的安全问题。

However, the pipe in bash will return true even if the first command fails, so this approach fails to detect if the command fails. 但是,即使第一个命令失败,bash中的管道也将返回true,因此该方法无法检测该命令是否失败。

That is not true. 那是不对的。
But I think you mean: the 'some_program --an-option | tee -a ' + logfile 但我认为您的意思是: 'some_program --an-option | tee -a ' + logfile 'some_program --an-option | tee -a ' + logfile exit status code always is 0 even though fails in any command part. 'some_program --an-option | tee -a ' + logfile退出状态代码始终为0,即使在任何命令部分中均失败。

Well, using multiple commands (when using && or || ) or connecting multiple commands together via pipes causes unreliable exit status code when returned. 好吧,使用多个命令(使用&&|| )或通过管道将多个命令连接在一起会导致返回时不可靠的退出状态代码。

Regardless, in the command: some_program --an-option | tee -a ' + logfile 无论如何,在命令中: some_program --an-option | tee -a ' + logfile some_program --an-option | tee -a ' + logfile logfile is not written if some_program fails. 如果some_program失败,则不会写入some_program --an-option | tee -a ' + logfile文件。 So you don't need to worry regarding exit code. 因此,您无需担心退出代码。

Anyway the best way to do pipe along with subprocess is creating Popen objects ans handling stdout and stdin : 无论如何,与子进程一起进行管道处理的最佳方法是创建Popen对象并处理stdoutstdin

import subprocess as sp 导入子进程为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

That is it! 这就对了!
I the last Popen we invoke Popen.communicate() to send the last output from ls command to tee command STDIN. 在最后一个Popen中,我们调用Popen.communicate()ls命令的最后输出发送到tee命令STDIN。

In the Python doc there's a tiny tutorial called Replacing shell pipeline , maybe you want take a look. 在Python文档中,有一个很小的教程,叫做《 替换外壳管道》 ,也许您想看看。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM