【问题标题】:Simple command line application in python - parse user input?python中的简单命令行应用程序-解析用户输入?
【发布时间】:2014-08-15 19:28:20
【问题描述】:

这里是python的新手-我想创建一个命令行应用程序,用户将在其中输入输入我将解析它并执行一些命令-在以下行中:

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

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

【问题讨论】:

  • 到目前为止您尝试过什么?另外,看看string.splittokenize 模块
  • init /path/to/dir 的预期输出是什么? argparse 用于解析命令行参数,因此很可能不是您要查找的内容。
  • @hlt:想使用 argparse - 而不是重新发明轮子。此外,我不确定是否没有用于命令行应用程序的模块-无论如何我都在重新发明轮子:)
  • @enrico.bacis:我希望我可以将它放在一行中进行解析
  • 你为什么忽略KeyboardInterrupt?它不会让你的程序更可靠,而且世界本身就是那么脆弱……

标签: python argparse


【解决方案1】:

你可以看一下cmd库:http://docs.python.org/library/cmd.html


如果你想自己解析,你可以使用split对用户输入进行tokenize,并根据token执行你的命令,如下所示:

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

【讨论】:

  • 我想避免使用“ifelseheimer”,而是在 argparse.Parser 中使用此逻辑
  • 为避免“ifelifelseinson”在try: ... except: ... 结构中使用dictionary_of_commands[command](args)
  • 或者你可以试试cmd lib
  • shlex.split 会正确处理init '/path/to/spacey dir'
  • Ipython 使用argparse 处理其主命令行,以及魔术命令的命令行。所以input -> shlex.split -> argparse当然是可能的。
【解决方案2】:

arparse 是您建议的完美解决方案。文档写得很好,并展示了许多如何简单调用它的示例。请记住,默认情况下它要读取 sys.argv,因此当您调用 parse_args 时,您希望给它 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")

【讨论】:

  • 嗯 - 我能以某种方式避免破折号吗?我也希望 argparse 将不同的 coomands 视为互斥的
  • 我很确定破折号会破坏交易。没有这些,您最好只接受其他人的 splitter() 建议。您还可以查看 shlex (docs.python.org/2/library/shlex.html),它为您提供了强大的解析能力,而无需 argparse 的开销。
  • 谢谢 - 互斥怎么样?
  • 可以通过使用subparsersargparse 来消除破折号。
  • 如果'init' 是某种命令,而'/path...' 是它的参数,那么用subparsers 设置argparse 是有意义的,特别是如果还有其他@987654330 @ ('init', 'build','list', 'quit', etc) 有自己的参数和选项。
【解决方案3】:

这取决于你想做什么,但你可以让你的脚本使用 ipython(交互式 python)。例如:

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

用法:启动脚本后,

init("pathToFile.txt")

您正在交互式 python 会话中运行,因此您会获得难以手动实现的选项卡完成等功能。另一方面,你被 python 语法困住了。这取决于您的应用程序。

【讨论】:

    【解决方案4】:

    我所做的是:

    # 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
    

    到目前为止,只尝试了一个命令,所以这是未经测试的。我使用了 cmets 的 shlex 和 subparsers 技巧。我查看了@jh314 建议的 cmd 模块,但并没有完全理解它 - 但我认为它是这项工作的工具 - 我对代码做我所做的但使用 cmd 模块的答案感兴趣。

    【讨论】:

      猜你喜欢
      • 2012-09-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多