Last active
July 7, 2016 13:18
-
-
Save alixedi/af585194437d54493e65 to your computer and use it in GitHub Desktop.
Python tool to make long CLI commands interactive
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 | |
''' | |
Nutshell - make long CLI commands interactive | |
--------------------------------------------- | |
I am lazy. I am forgetful. I often have to write long CLI commands. | |
The shell history does an awesome job of remembering them for me but too often | |
I end up working too hard in trying to find the line I wrote a couple of days | |
back and having to change something simlple e.g. name of a file etc. | |
With Nutshell I am trying to make a sidekick for alias. At its simplest: | |
$ python nutshell.py 'hadoop fs -cat hdfs:///#project/#package/#file | head -n#lines' | |
'project' > my-project | |
'package' > my-package | |
'file' > data.csv | |
'lines' > 5 | |
<Runs hadoop fs -cat hdfs:///my-project/my-package/data.csv | head -n5> | |
... | |
You can set environment variables starting with `NUTSHELL_` to pass in arguments like so: | |
$ export NUTSHELL_lines=10 | |
$ python nutshell.py 'hadoop fs -cat hdfs:///#project/#package/#file | head -n#lines' | |
'project' > my-project | |
'package' > my-package | |
'file' > data.csv | |
<Runs hadoop fs -cat hdfs:///my-project/my-package/data.csv | head -n10> | |
... | |
You can also pass in CLI arguments - useful for having your shell history just so: | |
$ export NUTSHELL_lines=10 | |
$ python nutshell.py 'hadoop fs -cat hdfs:///#project/#package/#file | head -n#lines' #file='foo.csv' | |
'project' > my-project | |
'package' > my-package | |
<Runs hadoop fs -cat hdfs:///my-project/my-package/foo.csv | head -n10> | |
... | |
Finally, CLI arguments override environment variables: | |
$ export NUTSHELL_lines=10 | |
$ python nutshell.py 'hadoop fs -cat hdfs:///#project/#package/#file | head -n#lines' #lines='8' | |
'project' > my-project | |
'package' > my-package | |
'file' > data.csv | |
<Runs hadoop fs -cat hdfs:///my-project/my-package/foo.csv | head -n8> | |
... | |
Enjoy! | |
''' | |
import os | |
import sys | |
from subprocess import Popen, PIPE, STDOUT | |
from string import Template | |
def _stdin23(prompt): | |
'''Takes input from user. Works for Python 2 and 3.''' | |
_v = sys.version[0] | |
return input(prompt) if _v is '3' else raw_input(prompt) | |
def env(env=None, prefix='NUTSHELL_'): | |
'''Reads env variables like NUTSHELL_<var_name>, parse | |
them and returns a dict. | |
>>> env_overrides(env={'yo': 0, 'A_yo': 1}, pre='A_') | |
{'yo': 1} | |
''' | |
env = env or os.environ | |
_vars = env.iteritems() | |
for k, v in env.iteritems(): | |
if k.startswith(prefix): | |
yield (k.lstrip(prefix), v) | |
def cli(args=None, prefix='#'): | |
'''Reads cli overrides like #<var_name>=<value>.''' | |
args = args or sys.argv[1:] | |
for arg in args: | |
if arg.startswith(prefix): | |
k, v = arg.split('=') | |
yield (k.lstrip(prefix), v) | |
class Nutshell(object): | |
def __init__(self, args, prefix='#', _input=_stdin23): | |
self.cmd = args[0] | |
self.overrides = args[1:] | |
self.prefix = prefix | |
self._input = _input | |
self.tpl_class = type("NutTpl", (Template,), {'delimiter': '#'}) | |
def render(self, overrides): | |
'''Tries to render template, fails by asking questions | |
if no overrides found.''' | |
tpl = self.tpl_class(self.cmd) | |
data = overrides or {} | |
while True: | |
try: | |
return tpl.substitute(**data) | |
except KeyError as e: | |
ans = str(self._input('%s > ' % e)) | |
data.update({e.message: ans}) | |
def run(self): | |
overrides = dict(list(env()) + list(cli())) | |
cmd = self.render(overrides) | |
print cmd | |
p = Popen(cmd, shell=True, stdout=PIPE, stderr=STDOUT) | |
p.wait() | |
return p.stdout.read().strip() | |
if __name__ == '__main__': | |
print Nutshell(sys.argv[1:]).run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment