Python 过滤tee写入文件的输出,但不过滤tee写入控制台的输出

Python 过滤tee写入文件的输出,但不过滤tee写入控制台的输出,python,bash,tee,Python,Bash,Tee,我知道有人问过类似的问题,但我找不到任何能解决这种特殊情况的方法 我有一系列不同的python脚本,它们使用日志记录库(打印到stderr)。这些脚本由bash脚本串在一起,bash脚本按顺序调用它们,并通过cron进行调度。在每次运行结束时,bash脚本通过电子邮件发送各种退出代码的摘要,并打算包含各种其他日志消息(即,如果出现严重的级别的日志记录,它应该到达我所称的“cron log”) 由于python代码本身在日志记录中非常冗长,我不想意外地给自己发送一个5-10MB的日志,我只希望有特

我知道有人问过类似的问题,但我找不到任何能解决这种特殊情况的方法

我有一系列不同的python脚本,它们使用
日志记录
库(打印到
stderr
)。这些脚本由bash脚本串在一起,bash脚本按顺序调用它们,并通过
cron
进行调度。在每次运行结束时,bash脚本通过电子邮件发送各种退出代码的摘要,并打算包含各种其他日志消息(即,如果出现
严重的
级别的日志记录,它应该到达我所称的“cron log”)

由于python代码本身在日志记录中非常冗长,我不想意外地给自己发送一个5-10MB的日志,我只希望有特定的输出将其发送到cron日志。我想使用
tee
从python过滤
stderr
日志记录,并且只将特定消息直接发送到cron日志,但所有消息仍应发送到控制台

为了再现性,假设我有以下bash脚本:

#!/bin/bash

LOGPFX="BASH"  # if a log msg has this term, put it in CRONLOG
CRONLOG="cronlog.txt"

exec 2> >(grep ${LOGPFX} | tee -a ${CRONLOG})

# run
python test.py
下面是
test.py
的内容:

import logging

def get_logger():
    logger = logging.getLogger("testlog")
    logger.setLevel(logging.INFO)
    lformat = "%(asctime)s - %(levelname)s - %(module)s - %(funcName)s - %(message)s"

    handler = logging.StreamHandler()
    handler.setLevel(logging.INFO)
    handler.setFormatter(logging.Formatter(lformat))

    logger.addHandler(handler)
    return logger

if __name__ == '__main__':
    logger = get_logger()
    logger.info("Should not appear in console but not cronlog.txt")
    logger.info("[BASH INFO] Should appear in both")
“[BASH INFO]应该出现在这两个”
消息中,并成功地将其发送到cron日志,但问题是控制台输出中完全忽略了第一条日志消息。如何过滤这些消息,使通过
grep
的消息进入控制台和日志,而所有其他消息只进入控制台

我知道这是一条需要改变的路线:

exec 2> >(grep ${LOGPFX} | tee -a ${CRONLOG})
cronlog.txt的内容

$ cat cronlog.txt 
2017-12-21 08:19:39,267 - INFO - test - <module> - [BASH INFO] Should appear in both

它正确地将所有内容定向到控制台,但没有将正确的消息写入日志(我发现这个:${1}
行打印一个空字符串)

tee
写入另一个进程替换(在写入之前执行grep)将完成以下操作:

# all-caps variable names are used for variables with meaning to the shell
# don't use them for names you assign yourself.
logpfx="BASH"  # if a log msg has this term, put it in CRONLOG
cronlog="cronlog.txt"

exec 3>&2 # backup original stderr on FD 3
exec 2> >(tee -a >(grep "$logpfx" >"$cronlog"))
请注意,在邮寄日志之前,您应该确保关闭文件描述符并让其刷新。因此,在收集要记录的内容后和发送电子邮件之前,您将希望运行:

exec 2>&3 # restore backup of original FD 3, so tee and grep can exit

ftee
显然已经坏了——请参阅,以获取从stdin读取的指南,而不是命令行(它从现在开始读取,尽管它的命令行上没有传递任何内容)。这太完美了!非常感谢您应该先
2>&3
(将stderr备份到另一个文件描述符),然后再
3>&2
?现在看来是倒退了,但那可能是我自己的理解力差。
3>&2
使fd3成为fd2的复制品。相反--
2>&3--将在FD 3处重定向实际未打开的stderr(FD 2)。请记住,
的方向决定是否打开右侧命名的文件或描述符进行读取或写入(如果左侧未给出FD,则默认值是否分别为
0
1
)。但哪个片段位于左侧或哪个片段位于右侧是常量。
exec 2>&3 # restore backup of original FD 3, so tee and grep can exit