【问题标题】:Parsing empty options in Python在 Python 中解析空选项
【发布时间】:2009-08-04 18:32:47
【问题描述】:

我有一个允许您将事件数据发送到自定义脚本的应用程序。您只需布置命令行参数并分配哪些事件数据与哪些参数一起使用。问题是这里没有真正的灵活性。您绘制的每个选项都将被使用,但并非每个选项都一定有数据。因此,当应用程序构建要发送到脚本的字符串时,一些参数是空白的,并且 python 的 OptionParser 错误输出为“错误:--someargument 选项需要一个参数”

由于有超过 200 个数据点,我不能编写单独的脚本来处理可能参数的每个组合(需要 2^200 个脚本)。有没有办法在 python 的 optionparser 中处理空参数?

【问题讨论】:

    标签: python optparse optionparser


    【解决方案1】:

    对不起,我的第一个答案误解了这个问题。您可以在定义选项时使用 callback 操作类型来实现为命令行标志提供可选参数的能力。使用以下函数作为回调(您可能希望根据自己的需要进行调整)并为每个可以选择接收参数的标志配置它:

    import optparse
    
    def optional_arg(arg_default):
        def func(option,opt_str,value,parser):
            if parser.rargs and not parser.rargs[0].startswith('-'):
                val=parser.rargs[0]
                parser.rargs.pop(0)
            else:
                val=arg_default
            setattr(parser.values,option.dest,val)
        return func
    
    def main(args):
        parser=optparse.OptionParser()
        parser.add_option('--foo',action='callback',callback=optional_arg('empty'),dest='foo')
        parser.add_option('--file',action='store_true',default=False)
        return parser.parse_args(args)
    
    if __name__=='__main__':
        import sys
        print main(sys.argv)
    



    从命令行运行你会看到:

    # python parser.py
    (<Values at 0x8e42d8: {'foo': None, 'file': False}>, [])
    
    # python parser.py --foo
    (<Values at 0x8e42d8: {'foo': 'empty', 'file': False}>, [])
    
    # python parser.py --foo bar
    (<Values at 0x8e42d8: {'foo': 'bar', 'file': False}>, [])
    

    【讨论】:

    • 这个变种的问题是--for=bar工作:parser.py: error: --foo option does not take a value
    【解决方案2】:

    是的,添加选项时有一个参数可以这样做:

    from optparse import OptionParser
    parser = OptionParser()
    parser.add_option("--SomeData",action="store", dest="TheData", default='')
    

    default 参数指定您希望选项具有的值,但可以选择有一个参数。

    【讨论】:

    • 我使用的是默认选项,但这仅在命令行中未指定 --SomeData 时使用。如果你这样做:--SomeData --SomeMoreData blah,然后--SomeData 错误,因为它没有传递一个值。
    • @directedition 对不起,我误解了你的问题。发布另一个答案,其中包含适合您的解决方案。
    【解决方案3】:

    我不认为optparse 可以做到这一点。 argparse 是一个不同的(非标准)模块,它可以在 handle situations like this 选项具有可选值的地方。

    对于optparse,您要么必须指定包含其值的选项,要么两者都省略。

    【讨论】:

      【解决方案4】:

      Optparse 已经允许您将空字符串作为选项参数传递。因此,如果可能,将空字符串视为“无值”。对于长选项,以下任何一项都有效:

      my_script --opt=   --anotheroption
      my_script --opt='' --anotheroption
      my_script --opt="" --anotheroption
      my_script --opt '' --anotheroption
      my_script --opt "" --anotheroption
      

      对于短样式选项,您可以使用以下任一选项:

      my_script -o '' --anotheroption
      my_script -o "" --anotheroption
      

      警告:这已经在 Linux 下进行了测试,在其他类 Unix 系统下应该也能正常工作; Windows 以不同方式处理命令行引用,并且可能不接受上面列出的所有变体。

      【讨论】:

      • 当然可以,但它与简单地使用my_script --opt不同。
      【解决方案5】:

      Mark Roddy 的解决方案可行,但它需要在运行时修改解析器对象的属性,并且不支持 - 或 - 以外的替代选项格式。 一个稍微少一点的解决方案是在运行 optparse 之前修改 sys.argv 数组,并在不需要参数的开关之后插入一个空字符串 ("")。 此方法的唯一限制是您的选项默认为可预测的值,而不是插入到 sys.argv 中的值(我在下面的示例中选择了 None,但这并不重要)。

      下面的代码创建一个示例解析器和一组选项,从解析器中提取一个允许开关的数组(使用一点实例变量魔法),然后遍历 sys.argv,每次找到一个 允许的开关,它检查它是否在没有任何参数的情况下给出。如果切换后没有参数,则在命令上插入空字符串 线。更改 sys.argv 后,解析器被调用,您可以检查值为 "" 的选项,并采取相应措施。

      #Instantiate the parser, and add some options; set the options' default values to None, or something predictable that
      #can be checked later.
      PARSER_DEFAULTVAL = None
      parser = OptionParser(usage="%prog -[MODE] INPUT [options]")
      #This method doesn't work if interspersed switches and arguments are allowed.
      parser.allow_interspersed_args = False
      parser.add_option("-d", "--delete", action="store", type="string", dest="to_delete", default=PARSER_DEFAULTVAL)
      parser.add_option("-a", "--add", action="store", type="string", dest="to_add", default=PARSER_DEFAULTVAL)
      
      #Build a list of allowed switches, in this case ['-d', '--delete', '-a', '--add'] so that you can check if something
      #found on sys.argv is indeed a valid switch. This is trivial to make by hand in a short example, but if a program has
      #a lot of options, or if you want an idiot-proof way of getting all added options without modifying a list yourself,
      #this way is durable. If you are using OptionGroups, simply run the loop below with each group's option_list field.
      allowed_switches = []
      for opt in parser.option_list:
          #Add the short (-a) and long (--add) form of each switch to the list.
          allowed_switches.extend(opt._short_opts + opt._long_opts)
      
      #Insert empty-string values into sys.argv whenever a switch without arguments is found.
      for a in range(len(sys.argv)):
          arg = sys.argv[a]
          #Check if the sys.argv value is a switch
          if arg in allowed_switches:
              #Check if it doesn't have an accompanying argument (i.e. if it is followed by another switch, or if it is last
              #on the command line)
              if a == len(sys.argv) - 1 or argv[a + 1] in allowed_switches:
                  sys.argv.insert(a + 1, "")
      
      options, args = parser.parse_args()
      
      #If the option is present (i.e. wasn't set to the default value)
      if not (options.to_delete == PARSER_DEFAULTVAL):
          if options.droptables_ids_csv == "":
              #The switch was not used with any arguments.
              ...
          else:
              #The switch had arguments.
              ...
      

      【讨论】:

        【解决方案6】:

        检查cp 命令是否理解例如--backup=simple不是 --backup simple,我这样回答问题:

        import sys
        from optparse import OptionParser
        
        def add_optval_option(pog, *args, **kwargs):
            if 'empty' in kwargs:
                empty_val = kwargs.pop('empty')
                for i in range(1, len(sys.argv)):
                    a = sys.argv[i]
                    if a in args:
                        sys.argv.insert(i+1, empty_val)
                        break
            pog.add_option(*args, **kwargs)
        
        def main(args):
            parser = OptionParser()
            add_optval_option(parser,
                              '--foo', '-f',
                              default='MISSING',
                              empty='EMPTY',
                              help='"EMPTY" if given without a value. Note: '
                              '--foo=VALUE will work; --foo VALUE will *not*!')
            o, a = parser.parse_args(args)
            print 'Options:'
            print '  --foo/-f:', o.foo
            if a[1:]:
                print 'Positional arguments:'
                for arg in a[1:]:
                    print ' ', arg
            else:
                print 'No positional arguments'
        
        if __name__=='__main__':
            import sys
            main(sys.argv)
        

        自我广告:这是我的thebops 包的opo 模块的一部分... ;-)

        【讨论】:

          猜你喜欢
          • 2022-09-29
          • 2013-12-21
          • 1970-01-01
          • 2016-02-13
          • 2021-09-14
          • 2013-03-25
          • 1970-01-01
          • 2017-01-08
          • 2021-02-18
          相关资源
          最近更新 更多