From 3a22bb1e9933ec7b8dd6ebee690b8fb711fd3f0b Mon Sep 17 00:00:00 2001 From: wirawan Date: Wed, 20 Jan 2010 21:27:41 +0000 Subject: [PATCH] * Added backward compatibility/support for python 2.3 or older. * Added more general-purpose (some coreutils, grep, less, etc.) Linux commands into the namespace for ease of use in regular python. --- shell_tools.py | 136 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 101 insertions(+), 35 deletions(-) diff --git a/shell_tools.py b/shell_tools.py index d779d53..3177978 100644 --- a/shell_tools.py +++ b/shell_tools.py @@ -1,4 +1,4 @@ -# $Id: shell_tools.py,v 1.3 2010-01-20 17:25:54 wirawan Exp $ +# $Id: shell_tools.py,v 1.4 2010-01-20 21:27:41 wirawan Exp $ # # wpylib.shell_tools # Created: 20100106 @@ -9,30 +9,35 @@ # import os -import subprocess +import os.path import sys +try: + import subprocess + has_subprocess = True +except: + if "has_subprocess" not in globals(): + print >>sys.stderr, "Newer subprocess module does not exist, using older interfaces." + has_subprocess = False + def mcd(subdir): # Assuming we have GNU coreutils' mkdir - cmd = ["mkdir", "-p", subdir] - try: - retcode = subprocess.call(cmd, shell=False) - if retcode == 0: - os.chdir(subdir) - return - - print >>sys.stderr, "mcd " + subdir + ": ", - if retcode < 0: - print >>sys.stderr, "mkdir was terminated by signal", -retcode - else: - print >>sys.stderr, "mkdir returned", retcode - raise RuntimeError, "Directory creation failure" - except OSError, e: - print >>sys.stderr, "mcd failed:", e - raise + mkdir("-p", subdir) + os.chdir(subdir) + +def provide_file(dest, src): + """Checks if file `dest' exists. If it does not, provide for it by means + of a softlink from `src'.""" + if not os.path.exists(dest): + # strip trailing /'s just in case it exists + os.symlink(src, dest.rstrip("/")) + + +# Low-level utilities: def errchk(cmd, args, retcode): + """Checking for error after the invocation of an external command.""" if retcode == 0: return print >>sys.stderr, "Error executing ", cmd, " ".join(args) @@ -43,26 +48,86 @@ def errchk(cmd, args, retcode): raise RuntimeError, err -def run(prg, args): - retcode = subprocess.call((prg,) + args) - errchk(prg, args, retcode) - return 0 - - -def pipe_out(args, split=False, shell=False): - """Executes a shell command, piping out the stdout to python for parsing. - This is my customary shortcut for backtick operator. - The result is either a single string (if split==False) or a list of strings - with EOLs removed (if split==True).""" - retval = subprocess.Popen(args, stdout=subprocess.PIPE, shell=shell).communicate()[0] - if not split: - return retval - else: - return retval.splitlines() +def quote_cmdline(seq): + """Quotes the strings in seq for feeding to shell. + This is a severe protection to prevent: + - variable, command, or other substitutions + - shell expansions (parameter, wildcard) + - word splitting + - invocation of shell builtin (!!!) + """ + # Python 2.6's subprocess.py has list2cmdline, but I don't like it because + # it still allows the shell to interpret wildcards. We have to quote wildcards + # (*, [], {}, ?) and $ as well. + rslt = [] + for i in seq: + inew = '"' + i.replace("\\", "\\\\").replace('"', '\\"').replace('$', '\\$').replace('`', '\\`') + '"' + rslt.append(inew) + return " ".join(rslt) + +if has_subprocess: + + def run(prg, args): + retcode = subprocess.call((prg,) + tuple(args)) + errchk(prg, args, retcode) + return 0 + + def pipe_out(args, split=False, shell=False): + """Executes a shell command, piping out the stdout to python for parsing. + This is my customary shortcut for backtick operator. + The result is either a single string (if split==False) or a list of strings + with EOLs removed (if split==True).""" + retval = subprocess.Popen(args, stdout=subprocess.PIPE, shell=shell).communicate()[0] + if not split: + return retval + else: + return retval.splitlines() + +else: + + def run(prg, args=()): + # Python < 2.4 does not have subprocess, so we use spawnvp + retcode = os.spawnvp(os.P_WAIT, prg, (prg,) + tuple(args)) + errchk(prg, args, retcode) + return 0 + + def pipe_out(args, split=False, shell=False): + """Executes a shell command, piping out the stdout to python for parsing. + This is my customary shortcut for backtick operator. + The result is either a single string (if split==False) or a list of strings + with EOLs removed (if split==True).""" + if shell or isinstance(args, str): + # BEWARE: args should be a string in this case + p = os.popen(args, "r") + else: + args = quote_cmdline(args) + p = os.popen(args, "r") + retval = p.read() + status = p.close() + if not split: + return retval + else: + return retval.splitlines() # coreutils - +# and other common utilities + +CMD = ['cat', 'cp', 'head', 'grep', 'less', 'ls', 'mkdir', 'mv', 'rm', 'tail'] +CMD_NAME = {} +for n in CMD: + CMD_NAME[n] = n + s = """def %(cmd)s(*args): run(CMD_NAME['%(cmd)s'], args)""" + exec(s % {'cmd': n }) + +def import_commands(namespace, cmds=None): + if cmds == None: cmds = CMD + thismod = globals() + for n in cmds: + n_act = thismod[n] + namespace.setdefault(n, n_act) + +""" def cp(*args): run('cp', args) @@ -71,4 +136,5 @@ def mkdir(*args): def mv(*args): run('mv', args) +"""