1

I need to write a command line application, like a shell. So it will include commands etc. The thing is I don't know how to pass parameters to the funcions in a module. For example:

User writes: function1 folder1 Program should now pass the 'folder1' parameter to the function1 function, and run it. But also it has to support other functions with different parameters ex:

User input: function2 folder2 --exampleparam

How to make this to work? I mean, I could just write a module, import it in python and just use the python console, but this is not the case. I need a script that takes command input and runs it.

I tried to use eval(), but that doesn't solve the problem with params. Or maybe it does but I don't see it?

6 Answers6

8

The first part of your problem -- parsing the command line -- can be solved with argparse.

The second part -- converting the string name of a function into a function call -- can be done with exec or a dispatching dict which maps from strings to function objects.

I would recommend NOT using exec for this, since allowing a user to call arbitrary Python functions from the command line might be dangerous. Instead, make a whitelist of allowable functions:

import argparse


def foo(path):
    print('Running foo(%r)' % (path, ))


def bar(path):
    print('Running bar(%r)' % (path, ))

dispatch = {
    'foo': foo,
    'bar': bar,
}

parser = argparse.ArgumentParser()
parser.add_argument('function')
parser.add_argument('arguments', nargs='*')
args = parser.parse_args()

dispatch[args.function](*args.arguments)

% test.py foo 1
Running foo('1')
% test.py bar 2
Running bar('2')
% test.py baz 3
KeyError: 'baz'

The above works when the command is typed into the command-line itself. If the command is being typed into stdin, then we'll need to do something a bit different.

A simple way would be to call raw_input to grab the string from stdin. We could then parse the string with argparse, as we did above:

shmod.py:

import argparse


def foo(path):
    print('Running foo(%r)' % (path, ))


def bar(path):
    print('Running bar(%r)' % (path, ))

dispatch = {
    'foo': foo,
    'bar': bar,
}

def parse_args(cmd):
    parser = argparse.ArgumentParser()
    parser.add_argument('function')
    parser.add_argument('arguments', nargs='*')
    args = parser.parse_args(cmd.split())
    return args

main.py:

import shmod

while True:
    cmd = raw_input('> ')
    args = shmod.parse_args(cmd)
    try:
        shmod.dispatch[args.function](*args.arguments)
    except KeyError:
        print('Invalid input: {!r}'.format(cmd))

Another, more sophisticated way to handle this is to use the cmd module, as @chepner mentioned in the comments.

from cmd import Cmd


class MyInterpreter(Cmd):

    prompt = '> '

    def do_prompt(self, line):
        "Change the interactive prompt"
        self.prompt = line + ': '

    def do_EOF(self, line):
        return True

    def do_foo(self, line):
        print('Running foo {l}'.format(l=line))

    def do_bar(self, line):
        print('Running bar {l}'.format(l=line))

if __name__ == '__main__':
    MyInterpreter().cmdloop()

For more information on how to use the cmd module, see Doug Hellman's excellent tutorial.


Running the code above yields a result like this:

% test.py
> foo 1
Running foo 1
> foo 1 2 3
Running foo 1 2 3
> bar 2
Running bar 2
> baz 3
*** Unknown syntax: baz 3
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • For the second part, take a look at the `cmd` module as well. – chepner Jan 25 '13 at 13:49
  • This works for me, but I still can't figure out how to actually launch the command from another script. If I place it into a function in a module, and invoke it like this: module.function, nothing happens. However if I do this: module.function 1, I get a syntax error. No point in invoking it with a parameter module.function(param), since the function doesn't have any. I'm new to Python, and don't really get it yet unfortunately. – Marcin Janeczek Jan 25 '13 at 14:24
  • Try `import module; module.function()` to call `function` which is defined in `module`. – unutbu Jan 25 '13 at 14:33
  • This is precisely what I've done, but that doesn't allow me to pass any parameters to the parser. Instead it just calls the function, without any params which, in this case, does nothing. In the example you would run the script with parameters from within the command line. I need to run it in this manner, but from another script. – Marcin Janeczek Jan 25 '13 at 14:37
  • Okay, I'm not understanding what it is you are asking. Could you post your code (both scripts) and what you want to happen? – unutbu Jan 25 '13 at 14:41
  • If it was me, I'd just use Python console as my shell, and simply write a module. Requirements are requirements tough. Since I'm only testing right now: I've got a shmod.py file, with your code inside a func() function. Also I've got a main.py file, which is my main script. 1. I import shmod into main 2. User inputs, for example: func 1 What should I do to invoke the function from shmod, and pass the parameter to the argparse code you gave me, so it displays Running foo('1') on the screen? – Marcin Janeczek Jan 25 '13 at 14:46
  • Is the user running `main.py func 1` from the command line, or is the user running `main.py` (pressing Enter) and then typing `func 1` into stdin? – unutbu Jan 25 '13 at 14:53
2

optparse is deprecated since python 2.7 and anyway argparse is much more flexible. The approach of unutbu is safe, but in case you provide whitelist, I would suggest you to let the user know which functions are accepted

dispatch = {
    'foo': foo,    
    'bar': bar,
}

parser = argparse.ArgumentParser()
parser.add_argument('function', choices=dispatch.keys() )

FYI: if the parsing is not too complicated, docopt looks like a very nice package

Francesco Montesano
  • 8,485
  • 2
  • 40
  • 64
1

How about sys.argv? For more advanced stuff check out argsparse. optparse seems depreciated now, but there's a lot of answers here about this question.

Community
  • 1
  • 1
Aram Kocharyan
  • 20,165
  • 11
  • 81
  • 96
1

Take a look at the optparse module in python. It's exactly what you would need:

http://docs.python.org/2/library/optparse.html

Or you can write your own custom opt-parser (minimalistic though)

def getopts(argv):
   opts = {}
   while argv:
      if argv[0][0] == '-': # find "-name value" pairs
         opts[argv[0]] = argv[1] # dict key is "-name" arg
         argv = argv[2:]
      else:
         argv = argv[1:]
   return opts

if __name__ == '__main__':
from sys import argv # example client code
myargs = getopts(argv)
# DO something based on your logic here

But in case your script needs to run on python 3 and beyond, you need to consider argparse module.\

Hope that helps.

Manmohan Singh
  • 439
  • 1
  • 3
  • 15
0

Take a look at optparse . This can help passing and receiving shell style parameters to python scripts.

Update: Apparently optparse is deprecated now and argparse is now preferred option for parsing command line arguments.

MoveFast
  • 3,011
  • 2
  • 27
  • 53
0
import sys

def main(arg):
    return arg

print main(sys.argv[1])

where sys.argv[0] is the .py file you're running, and all the ones after it would be each argument. you could check the length of the list, then iterate through it, and parse them as necessary and pass the correct things to each function

DanielCardin
  • 545
  • 2
  • 8
  • 17