From ed3d52b0382c51c10dd8184e7f9258b0e9a4af1d Mon Sep 17 00:00:00 2001 From: Johann Dreo Date: Mon, 22 Sep 2014 22:38:43 +0200 Subject: [PATCH] allow several space-separated commands --- README.md | 40 ++++++++++++++++++++++++++++++++++++++++ paternoster.py | 26 +++++++++++--------------- 2 files changed, 51 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index bd253d3..8554366 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,43 @@ paternoster =========== A command line tool that call the given command for each input line matching the given regular expression pattern. + + +## SYNOPSIS + +`paternoster` [-h] + +`paternoster` [-w] PATTERN COMMAND(S) + + +## DESCRIPTION + +`paternoster` read lines of text stream on the standard input and call +*COMMAND(S)* for lines matching a given regular expression *PATTERN*. + +If groups are specified in the regular expression pattern, only them are taken +into account, else the whole matching pattern is considered. + +You can specify several commands when using groups by separating them with +spaces. If you indicate more commands than groups, the last ones will be +silently ignored. If you ask for fewer commands, the last one will be +duplicated across remaining groups. + + +## EXAMPLES + +* Make the speakers beep for every FIXME found in a source code: + `cat source.ext | paternoster "FIXME" "beep" + +* Play a sound for each error or warning on a compiler output (you must have the + corresponding wav files somewhere in you path): + `make 2>&1 | paternoster ".*(error|warning): .*$" "play %s.wav"` + +* Say out loud the error in a compiler output: + `make 2>&1 | paternoster ".*(error: .*)$" "espeak --punct=';{}()' '%s'"` + + +## CREDITS + +* Error sound by tcpp, licensed under CC-BY: http://www.freesound.org/people/tcpp/sounds/151309/ +* diff --git a/paternoster.py b/paternoster.py index 5003483..447d9cb 100755 --- a/paternoster.py +++ b/paternoster.py @@ -16,37 +16,33 @@ def call( cmd, text, nowait=False ): # If there is a string formatting mark try: # put the matching text in it - order = str(cmd % text).split() + behest = cmd % text except TypeError: # else, do not - order = cmd.split() - - # Join the command parts in the subprocess list format: [command,args] - behest = [order[0]," ".join(order[1:])] + behest = cmd with open("/dev/null") as dev_null: if nowait: - subprocess.Popen( behest, stdout=sys.stdout, stderr=dev_null ) + subprocess.Popen( behest, stdout=sys.stdout, stderr=dev_null, shell=True ) else: - subprocess.call( behest, stdout=sys.stdout, stderr=dev_null ) + subprocess.call( behest, stdout=sys.stdout, stderr=dev_null, shell=True ) -def parse( text, pattern, cmd="", nowait=False ): +def parse( text, pattern, cmd=[""], nowait=False ): regex = re.compile(pattern) for match in regex.finditer(text): # If no groups are specified if not match.groups(): - call( cmd, text, nowait ) + call( cmd[0], text, nowait ) else: nb_groups = len(match.groups()) # Build a list of colors that match the number of grouped, # If there is not enough commands, duplicate the last one. - cmds_l = cmd.split(",") - group_cmds = cmds_l + [cmds_l[-1]] * (nb_groups - len(cmds_l)) + group_cmds = cmd + [cmd[-1]] * (nb_groups - len(cmd)) # For each group index. # Note that match.groups returns a tuple (thus being indexed in [0,n[), @@ -109,18 +105,18 @@ if __name__ == "__main__": parser.add_argument("pattern", metavar="REGEX", type=str, nargs=1, help="A regular expression") - parser.add_argument("command", metavar="CMD", type=str, nargs='?', + parser.add_argument("commands", metavar="CMD", type=str, nargs="+", default="espeak %s", help="The command to run if REGEX match. \ If CMD contains a string formating placefolder (like \"%%s\"), \ it will be replaced by the matching text. \ - Multiple commands may be specified as a list of comma-separated values. \ - Commands will then be called for each matching group within REGEX.") + Multiple commands may be specified as a list of space-separated strings. \ + Each command will then be called for each corresponding matching group within REGEX.") parser.add_argument("-w", "--no-wait", action="store_true", help="Don't wait for the end of the current command before calling the next one.") args = parser.parse_args() - map_write( sys.stdin, sys.stdout, parse, args.pattern[0], args.command, args.no_wait ) + map_write( sys.stdin, sys.stdout, parse, args.pattern[0], args.commands, args.no_wait )