#!/usr/bin/env python # -*- coding: utf-8 mode: python -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 u"""Afficher des lignes en mettant en surbrillance certains patterns""" import sys, subprocess, re from collections import OrderedDict def bluef(line, *ignored): return '\x1B[34m%s\x1B[0m' % line def greenf(line, *ignored): return '\x1B[32m%s\x1B[0m' % line def yellowf(line, *ignored): return '\x1B[33m%s\x1B[0m' % line def redf(line, *ignored): return '\x1B[31m%s\x1B[0m' % line def nonef(line, *ignored): return re.sub('\x1B\[.*?m', '', line) DEFAULT_PATTERNS = OrderedDict([ (r'(?i)error', redf), (r'(?i)warn(ing)?', yellowf), (r'(?i)info', bluef), (None, nonef), ]) FORMATS = OrderedDict([ ('blue', bluef), ('green', greenf), ('yellow', yellowf), ('red', redf), ('none', nonef), ]) FORMAT_ALIASES = { 'b': 'blue', 'g': 'green', 'y': 'yellow', 'r': 'red', '': 'none', } def strip_nl(s): if s is None: return None elif s.endswith("\r\n"): s = s[:-2] elif s.endswith("\n"): s = s[:-1] elif s.endswith("\r"): s = s[:-1] return s def run_tailor(inputfile=None, patterns=None): if inputfile is None: def next_line(): while True: try: line = sys.stdin.readline() except: break if line == '': break yield line else: def next_line(): p = subprocess.Popen( ['tail', '-f', inputfile], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) while True: try: line = p.stdout.readline() except: break if line == '': break yield line if patterns is None: patterns = DEFAULT_PATTERNS for line in next_line(): line = nonef(strip_nl(line)) func = False mo = None for p, f in patterns.items(): if p is None: func = f mo = None break mo = re.search(p, line) if mo is not None: func = f break if func is not False: line = func(line, mo) sys.stdout.write(line) sys.stdout.write('\n') if __name__ == '__main__': from argparse import ArgumentParser, RawTextHelpFormatter AP = ArgumentParser( formatter_class=RawTextHelpFormatter, usage=u"%(prog)s [-f INPUTFILE]", description=__doc__, ) AP.set_defaults(inputfile=None, patterns=None, defaults=True) AP.add_argument('-f', '--follow', dest='inputfile', help=u"Spécifier un fichier dont il faut suivre le contenu") AP.add_argument('-e', '--pattern', action='append', dest='patterns', metavar='PATTERN:FORMAT', help=u"""\ Ajouter une spécification de pattern et le format dans lequel il doit être affiché Les formats valides sont: %s Le format par défaut est red""" % ', '.join(FORMATS.keys())) AP.add_argument('-z', '--no-defaults', action='store_const', dest='defaults', const=False, help=u"Ne pas ajouter les patterns par défaut") o = AP.parse_args() if o.patterns is None: patterns = DEFAULT_PATTERNS else: patterns = OrderedDict() for pf in o.patterns: mo = re.match('(.*):([a-zA-Z]*)$', pf) if mo is not None: p = mo.group(1) of = mo.group(2) else: p = pf of = 'red' f = of.lower() f = FORMAT_ALIASES.get(f, f) if f not in FORMATS: raise ValueError("%s: format invalide" % of) patterns[p] = FORMATS[f] if o.defaults: patterns.update(DEFAULT_PATTERNS) run_tailor(o.inputfile, patterns)