如何过滤Python屏幕打印?

时间:2017-04-05 17:47:51

标签: python bash

我想通过我的Python程序过滤屏幕打印。

理想情况下,指定一个“打印黑名单”会很好,例如这个。

*FutureWarning*
INFO*

当部分屏幕打印输出与上述模式之一匹配时,整个打印输出将被过滤掉。

我很惊讶没有人提出这个问题,因为我认为这非常有用,因为没有它,我需要经历不同类型的屏幕打印并相应地处理它们。你可以想象其中一些可能是由于print,有些是warning等等。

我正在使用bash运行我的Python脚本,所以也欢迎基于bash的方法!

3 个答案:

答案 0 :(得分:1)

您可以将print变量绑定到您的自定义函数,以检查您不想看到的任何输出。

以下是一个例子:

>>> def print(*args, **kwargs):
...     import builtins
...     if not any(str(s).startswith('INFO') for s in args):
...             builtins.print(*args, **kwargs)   

答案 1 :(得分:1)

你说你愿意做bash。您可以编写另一个脚本,例如filter.py

from sys import stdin

def print_filtered(line):
    if line.startswith("print this"):
        print(line, end='') # The line includes its own newline character

for line in stdin:
    print_filtered(line)

当然,您可以在print_filtered()中添加所需的任何过滤选项。你甚至可以做一些替换;也许你想在打印之前删除字符串的print this部分。

现在任何你想要过滤输出的程序,都可以这样运行:

$ python myfile.py | python filter.py

Rishav在评论中也提到可以用自定义对象替换sys.stdout。我不是很喜欢这个想法,但你可以用这样的东西做到这一点:

import sys

class FilteredPrinter(object):
    def __init__(self, filtered_print, stdout):
        self._filtered_print = filtered_print
        self._stdout = stdout

    def _write(self, string):
        self._filtered_print(string, self._stdout)

    def __getattr__(self, attr):
        if attr == 'write':
            return self._write
        return getattr(self._stdout, attr)


def filtered_print(string, stdout):
    if string == "\n" or string.startswith("print this"):
        stdout.write(string)

sys.stdout = FilteredPrinter(filtered_print, sys.stdout)
print("print this: my text") 
print("print this text")
print("Don't print this")

使用此解决方案,您可以指定所需的任何过滤器;您也可以使用sys.stderr执行此操作。如果您想返回旧版,请使用sys.stdout = sys.stdout._stdout。请注意,print函数为每个参数调用sys.stdout.write()一次,并再次为换行符调用print("first", "second")函数。也就是说,调用sys.stdout.write("first") sys.stdout.write(" ") sys.stdout.write("second") sys.stdout.write("\n") 将执行此操作:

{{1}}

因此,在编写过滤函数时,请务必牢记这一点。

答案 2 :(得分:1)

  

我想通过我的Python程序过滤屏幕打印。

您可以使用Python的logging模块,通过它输出所有打印和警告输出:

# in your main module
import logging

# -- log all output to myapp.log
logging.basicConfig(filename='myapp.log', level=logging.DEBUG)
# -- OR log all output to console
logging.basicConfig(level=logging.DEBUG)
# route all output to logging
logging.captureWarnings(True)
print  = logging.info  

添加您自己的Filter以根据所需关键字过滤所有输出:

# define Filter in its own module, say logfilter.py
class OutputFilter(logging.Filter):
    def __init__(self, keywords, name=None):
        super(OutputFilter, self).__init__(name)
        self.keywords = keywords

    def filter(self, record):
        msg = record.getMessage()
        return not any(k in msg for k in self.keywords)

# add this in your main module
from logfilter import OutputFilter
flt = OutputFilter(['*FutureWarning*', 'INFO*'])
logger = logging.getLogger() # root logger
logger.addFilter(flt)
warnLogger = logging.getLogger('py.warnings') # warnings logger
warnLogger.addFilter(flt)
  

(...)没有它,我需要经历不同类型的屏幕打印输出并相应地处理它们。

如果您可以更改源代码,最好始终使用模块记录器而不是print:

# in any module with output, right after imports
logger = logging.getLogger(__name__)
...
# instead of print
logger.info(...)
logger.error(...)
logger.debug(...)

优点是logging允许您对输出内容和位置进行非常精细的控制,同时从中心位置配置所有内容。

例如,虽然上面使用适用于所有日志记录输出的根记录器,但应用程序的每个模块都可以拥有自己的记录器,具有特定的配置,例如

# somewhere in your main module
# -- basic config is to console
logging.basicConfig(level=logging.DEBUG)
# -- module A output should go into its own file
logger = logging.getLogger('path.to.module.A')
handler = logging.FileHandler(filename='myapp-A.log') # see above
logger.setHandler(handler) 
logger.setLevel(logging.INFO)

# path/to/module/A.py
# -- right at the top of the module
import logging
logger = logging.getLogger(__name__)
...
# in the actual code
logger.info('info message')

此示例将模块A的所有INFO消息路由到文件myapp-A.log,而所有其他输出都发送到终端。

注意:从Python Logging Cookbook采用的所有示例。另请查看tutorial以获得更深入的解释。