Python - 在命令行应用中传递参数

1 投票
6 回答
3782 浏览
提问于 2025-04-17 13:39

我需要写一个命令行应用程序,就像一个小型的命令行工具。这个工具会包含一些命令等等。问题是,我不知道怎么把参数传递给模块里的函数。例如:

用户输入:function1 folder1

程序现在应该把'folder1'这个参数传递给function1函数,并执行它。但它还需要支持其他函数和不同的参数,比如:

用户输入:function2 folder2 --exampleparam

我该怎么做到这一点呢?我的意思是,我可以写一个模块,在Python里导入它,然后直接使用Python控制台,但这不是我想要的。我需要一个脚本,能够接受命令输入并执行。

我试过使用eval(),但这并没有解决参数的问题。或者说它解决了,但我没看出来?

6 个回答

1

看看Python里的optparse模块吧。这个模块正好是你需要的:

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

或者你也可以自己写一个简单的选项解析器(虽然功能比较少)

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

不过,如果你的脚本需要在Python 3及以后的版本上运行,你就得考虑一下argparse模块了。

希望这些对你有帮助。

2

optparse 从 Python 2.7 开始就不再推荐使用了,而且 argparse 更加灵活。unutbu 的方法是安全的,但如果你提供了一个白名单,我建议你让用户知道哪些函数是被允许的。

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

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

顺便提一下,如果解析的内容不太复杂,docopt 看起来是一个很不错的工具。

8

你遇到的第一个问题是解析命令行,这个可以用argparse来解决。

第二个问题是把函数的名字字符串转换成实际调用这个函数,这可以通过exec或者一个映射字符串到函数对象的字典来实现。

我建议不要使用exec,因为允许用户从命令行调用任意的Python函数可能会很危险。相反,应该建立一个允许调用的函数白名单:

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'

以上方法适用于直接在命令行中输入命令的情况。如果命令是通过stdin输入的,那我们需要采取一些不同的做法。

一个简单的方法是调用raw_input来从stdin获取字符串。然后我们可以像之前那样用argparse来解析这个字符串:

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))

还有一种更复杂的处理方式,就是使用cmd模块,正如@chepner在评论中提到的。

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()

想了解如何使用cmd模块,可以查看Doug Hellman的优秀教程。


运行上面的代码会得到类似这样的结果:

% 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

撰写回答