Python简单命令行应用 - 如何解析用户输入?

1 投票
4 回答
6044 浏览
提问于 2025-04-18 17:26

我刚接触Python,想做一个命令行应用程序,用户可以在里面输入内容,我会解析这些内容并执行一些命令。大概是这样的:

try:
    while True:
        input = raw_input('> ')
        # parse here
except KeyboardInterrupt:
    pass

用户应该输入像 init /path/to/dir 这样的命令。我可以用 argparse 来解析这些命令吗?我的方法是不是太简单粗暴了?

4 个回答

1

我做的事情是:

# main
parser = Parser('blah')
try:
    while True:
        # http://stackoverflow.com/a/17352877/281545
        cmd = shlex.split(raw_input('> ').strip())
        logging.debug('command line: %s', cmd)
        try:
            parser.parse(cmd)
        except SystemExit: # DUH http://stackoverflow.com/q/16004901/281545
            pass
except KeyboardInterrupt:
    pass

这里是解析器:

class Parser(argparse.ArgumentParser):
    def __init__(self, desc, add_h=True):
        super(Parser, self).__init__(description=desc, add_help=add_h,
                                     formatter_class=argparse.
                                    ArgumentDefaultsHelpFormatter)
        # https://docs.python.org/dev/library/argparse.html#sub-commands
        self.subparsers = subparsers = self.add_subparsers(
            help='sub-command help')
        # http://stackoverflow.com/a/8757447/281545
        subparsers._parser_class = argparse.ArgumentParser
        from  watcher.commands import CMDS
        for cmd in CMDS: cmd()(subparsers)

    def parse(self, args):
        return self.parse_args(args)

还有一个命令(CMDS=[watch.Watch]):

class Watch(Command):

    class _WatchAction(argparse.Action):
        def __call__(self, parser, namespace, values, option_string=None):
            # here is the actual logic of the command
            logging.debug('%r %r %r' % (namespace, values, option_string))
            setattr(namespace, self.dest, values)
            Sync.addObserver(path=values)

    CMD_NAME = 'watch'
    CMD_HELP = 'Watch a directory tree for changes'
    ARGUMENTS = {'path': Arg(hlp='Path to a directory to watch. May be '
                                  'relative or absolute', action=_WatchAction)}

其中:

class Command(object):
    """A command given by the users - subclasses must define  the CMD_NAME,
    CMD_HELP and ARGUMENTS class fields"""

    def __call__(self, subparsers):
        parser_a = subparsers.add_parser(self.__class__.CMD_NAME,
                                         help=self.__class__.CMD_HELP)
        for dest, arg in self.__class__.ARGUMENTS.iteritems():
            parser_a.add_argument(dest=dest, help=arg.help, action=arg.action)
        return parser_a

class Arg(object):
    """Wrapper around cli arguments for a command"""

    def __init__(self, hlp=None, action='store'):
        self.help = hlp
        self.action = action

到目前为止我只尝试了一个命令,所以这个还没经过充分测试。我参考了评论中的shlex和子解析器的建议。我看了@jh314推荐的cmd模块,但没完全理解。不过我觉得这个模块是合适的工具。我希望能看到一个用cmd模块实现我所做的事情的代码。

1

这要看你想做什么,但你可以让你的脚本使用ipython(交互式python)。比如说:

    #!/bin/ipython -i
    def init(path_to_dir):
        print(path_to_dir)

使用方法:在启动脚本后,

init("pathToFile.txt")

你会进入一个交互式的python会话,这样你就可以享受到像自动补全这样的功能,这些功能手动实现起来会比较麻烦。不过,你需要遵循python的语法。这一切都要看你的具体需求。

2

argparse是一个非常适合你提到的需求的工具。它的文档写得很好,里面有很多简单的例子教你怎么使用。需要注意的是,它默认会读取系统的命令行参数(sys.argv),所以当你调用parse_args的时候,要给它传入参数(https://docs.python.org/2.7/library/argparse.html?highlight=argparse#the-parse-args-method)。

唯一的缺点是,argparse期望你输入的参数是“参数格式”,也就是说,参数前面要加上短横线。

>>> import argparse
>>> parser = argparse.ArgumentParser(prog='PROG')
>>> parser.add_argument('-init', nargs=1)
>>> parser.parse_args('-init /path/to/something'.split())
Namespace(init="/path/to/something")
4

你可以看看这个命令行库:http://docs.python.org/library/cmd.html


如果你想自己解析输入,可以使用 split 方法把用户输入的内容分割成小块,然后根据这些小块来执行你的命令,类似这样:

try:
    while True:
        input = raw_input('> ')
        tokens = input.split()
        command = tokens[0]
        args = tokens[1:]
        if command == 'init':
            # perform init command
        elif command == 'blah':
            # perform other command


except KeyboardInterrupt:
    pass

撰写回答