Created
February 10, 2013 16:19
-
-
Save dgulino/4750088 to your computer and use it in GitHub Desktop.
Python script that runs one-liner python scripts similarly to how Perl runs them portions based on http://code.activestate.com/recipes/437932-pyline-a-grep-like-sed-like-command-line-tool/
(Graham Fawcett, Jacob Oscarson, Mark Eichin)
interface inspired by Perl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
""" | |
Python script that runs one-liner python scripts similarly to how Perl runs them | |
portions based on http://code.activestate.com/recipes/437932-pyline-a-grep-like-sed-like-command-line-tool/ | |
(Graham Fawcett, Jacob Oscarson, Mark Eichin) | |
interface inspired by Perl | |
""" | |
""" | |
Copyright (C) 2010 Drew Gulino | |
This program is free software: you can redistribute it and/or modify | |
it under the terms of the GNU General Public License as published by | |
the Free Software Foundation, either version 3 of the License, or | |
(at your option) any later version. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License | |
along with this program. If not, see <http://www.gnu.org/licenses/>. | |
""" | |
import sys | |
import re | |
import getopt | |
from getopt import GetoptError | |
import os | |
def usage(progname): | |
print "Usage: " + progname | |
version() | |
print """ | |
./pyliner (options) "script" | |
-a enables auto-split of input into the F[] array. | |
-F specifies the characters to split on with the -a option (default=" ") | |
-p loops over and prints input. | |
-n loops over and does not print input. | |
-l strips newlines on input, and adds them on output | |
-M lets you specify module imports | |
-o print out parsed and indented script on multiple lines | |
-e (assumed - does nothing, added for Perl compatibility) | |
variables: | |
F[] = split input | |
line = current input line | |
NR = current input line number | |
functions: | |
I(x) = function returns F[x] cast to integer | |
F(x) = function returns F[x] cast to float | |
script: | |
newlines are specified with ";" | |
":" will start an indent for the following lines | |
"?;" will add a newline and dedent the following lines | |
"/;" will reset the indent the far left | |
"BEGIN:" starts the an initial script not run in the input loop | |
"END:" starts part of the script run after the input loop | |
"#/" ends both the "BEGIN:" and "END:" portions of the script | |
""" | |
#test.txt= | |
""" | |
10,test | |
1,rest | |
100,test | |
10,best | |
1000,we | |
1,see | |
""" | |
""" | |
cat test.txt | ./pyliner.py -F"," -lane 'BEGIN:sum=0#/if I(0) > 90:sum+=I(0) END:print sum#/' | |
1100 | |
""" | |
""" | |
pyliner: | |
cat test.txt | ./pyliner.py -F"," -lane 'BEGIN:sum=0#/a=F(0);if a > 90:sum+=a END:print sum/NR#/' | |
183.333333333 | |
perl: | |
cat test.txt | perl -F"," -lane 'BEGIN {$sum=0; } ;$sum=$sum+=@F[0] if @F[0]>90;END { print $sum/$.; }' | |
183.333333333333 | |
""" | |
def version(): | |
print "version: 1.0" | |
write = sys.stdout.write | |
def indent_script(script_lines): | |
#indent lines | |
indent = 0 | |
next_indent = 0 | |
script = [] | |
for cmd in script_lines: | |
cmd = cmd.strip() | |
if cmd[-1]==(":"): | |
indent=indent+1 | |
if cmd[-1]=="?": | |
cmd=cmd[0:-1] | |
indent=indent-1 | |
if cmd[-1]=="/": | |
cmd=cmd[0:-1] | |
indent=0 | |
cmd=("\t"*next_indent)+cmd | |
next_indent = indent | |
script.append(cmd) | |
script = '\n'.join(script) | |
return script | |
def split_script(cmd): | |
#split lines | |
cmd = cmd.replace("?;","?\n") | |
cmd = cmd.replace("/;","/\n") | |
cmd = cmd.replace(";","\n") | |
cmd = cmd.replace(":",":\n") | |
return cmd | |
def main(argv, stdout, environ): | |
progname = argv[0] | |
split_lines=False | |
FS = ' ' | |
print_input = False | |
strip_newlines = False | |
no_print_input = False | |
output_script = False | |
F =[] | |
def I(x): return int(F[x]) | |
def f(x): return float(F[x]) | |
# parse options for module imports | |
try: | |
opts, args = getopt.getopt(sys.argv[1:], 'oaplneM:F:') | |
except GetoptError: | |
print "Invalid Option!" | |
usage(progname) | |
return | |
opts = dict(opts) | |
if '-M' in opts: | |
for imp in opts['-M'].split(','): | |
locals()[imp] = __import__(imp.strip()) | |
if "-a" in opts: | |
split_lines = True | |
if '-F' in opts: | |
FS = opts['-F'] | |
if "-p" in opts: | |
print_input = True | |
if "-l" in opts: | |
strip_newlines = True | |
if "-n" in opts: | |
no_print_input = True | |
if "-e" in opts: | |
pass | |
if "-o" in opts: | |
output_script = True | |
cmd = ' '.join(args) | |
if not cmd.strip(): | |
cmd = 'line' # no-op | |
begin_cmd = '' | |
end_cmd = '' | |
if cmd.find("BEGIN:") >= 0: | |
start=cmd.find("BEGIN:") | |
end=cmd.find("#/") | |
begin_cmd=cmd[start+6:end] | |
cmd=cmd[end+2:] | |
if cmd.find("END:") >= 0: | |
start=cmd.find("END:") | |
end=cmd.find("#/") | |
end_cmd=cmd[start+4:end] | |
cmd=cmd[:start] | |
cmd = split_script(cmd) | |
script_lines = cmd.splitlines() | |
if len(begin_cmd) > 1: | |
begin_cmd = split_script(begin_cmd) | |
begin_lines = begin_cmd.splitlines() | |
begin_script = indent_script(begin_lines) | |
begin_codeobj = compile(begin_script, 'command', 'exec') | |
result = eval(begin_codeobj, globals(), locals()) | |
if result is None or result is False: | |
result = '' | |
elif isinstance(result, list) or isinstance(result, tuple): | |
result = ' '.join(map(str, result)) | |
else: | |
result = str(result) | |
write(result) | |
if len(end_cmd) > 1: | |
end_cmd = split_script(end_cmd) | |
end_lines = end_cmd.splitlines() | |
end_script = indent_script(end_lines) | |
end_codeobj = compile(end_script, 'command', 'exec') | |
script = indent_script(script_lines) | |
if output_script == True: | |
print "BEGIN:" | |
print begin_script | |
print script | |
print "END:" | |
print end_script | |
#compile script | |
codeobj = compile(script, 'command', 'exec') | |
if print_input or no_print_input: | |
for num, line in enumerate(sys.stdin): | |
#line = line[:-1] | |
NR = num + 1 | |
if strip_newlines == True: | |
line = line.strip() | |
if print_input == True: | |
print(line) | |
if split_lines == True: | |
F = [w for w in line.split(FS) if len(w)] | |
result = eval(codeobj, globals(), locals()) | |
if result is None or result is False: | |
continue | |
elif isinstance(result, list) or isinstance(result, tuple): | |
result = ' '.join(map(str, result)) | |
else: | |
result = str(result) | |
write(result) | |
if strip_newlines == True: | |
write('\n') | |
else: | |
result = eval(codeobj, globals(), locals()) | |
if len(end_cmd) > 1: | |
result = eval(end_codeobj, globals(), locals()) | |
if result is None or result is False: | |
result = '' | |
elif isinstance(result, list) or isinstance(result, tuple): | |
result = ' '.join(map(str, result)) | |
else: | |
result = str(result) | |
write(result) | |
if __name__ == "__main__": | |
main(sys.argv, sys.stdout, os.environ) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Couple notes:
Code objects with 'exec' as the mode will always return None from eval(), so the loop will always 'continue'.
The -p option doesn't behave like perl as it just prints unmodified input prior to executing the code.
The -o option doesn't work unless you specify both a BEGIN and END block.
Neat idea overall, though.