Skip to content

Instantly share code, notes, and snippets.

@dgulino
Created February 10, 2013 16:19
Show Gist options
  • Save dgulino/4750088 to your computer and use it in GitHub Desktop.
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
#!/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
print script
print
print "END:"
print end_script
print
#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)
@ursetto
Copy link

ursetto commented Nov 11, 2013

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment