Can Python's argparse permute argument order like gnu getopt?
gnu getopt和使用它的命令行工具允许选项和参数交错,称为Permuting选项(请参见http://www.gnu.org/software/libc/manual/html_node/using getopt.html_using getopt)。Perl的getopt::long模块也支持这一点(使用qw(:config gnu_getopt))。argparse似乎不支持(甚至不提)排列选项。
有很多与arg/opt顺序相关的问题,但似乎没有一个能回答这个问题:argparse是否可以像getopt一样按排列参数顺序?
用例是一个典型的命令行签名,如gnu-sort:
1 | sort [opts] [files] |
其中1)选项和文件被排列,2)文件列表可以包含零个或多个参数。
例如:
1 2 3 4 5 6 7 8 9 | import argparse p = argparse.ArgumentParser(); p.add_argument('files',nargs='*',default=['-']); p.add_argument('-z',action='store_true') p.parse_args(['-z','bar','foo']) # ok p.parse_args(['bar','foo','-z']) # ok p.parse_args(['bar','-z','foo']) # not okay usage: ipython [-h] [-z] [files [files ...]] |
我试过了:
- parse-known-args——不会抱怨,但实际上也不会改变,也不会因为看起来像无效选项的参数而犹豫(例如,上面的--bogus或-b)。
- p.add_参数('files',nargs=argparse.remals)--选项-z包含在文件中,除非位置参数之前
- p.add_参数('files',nargs='*',action='append');
我想实现一些接近上面GNU排序原型的东西。我对可以为每个文件指定的标志(例如-f file1-f file2)不感兴趣。
这里有一个快速的解决方案,它一次解码一对参数列表(选项、位置参数)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | import argparse class ExtendAction(argparse.Action): def __call__(self, parser, namespace, values, option_string=None): items = getattr(namespace, self.dest, None) if items is None: items = [] items.extend(values) setattr(namespace, self.dest, items) parser = argparse.ArgumentParser() parser.add_argument('files', nargs='*', action=ExtendAction) parser.add_argument('-z', action='store_true') parser.add_argument('-v', action='count') parser.add_argument('args_tail', nargs=argparse.REMAINDER) def interleaved_parse(argv=None): opts = parser.parse_args(argv) optargs = opts.args_tail while optargs: opts = parser.parse_args(optargs, opts) optargs = opts.args_tail return opts print(interleaved_parse('-z bar foo'.split())) print(interleaved_parse('bar foo -z'.split())) print(interleaved_parse('bar -z foo'.split())) print(interleaved_parse('-v a -zv b -z c -vz d -v'.split())) |
输出:
1 2 3 4 | Namespace(args_tail=[], files=['bar', 'foo'], v=None, z=True) Namespace(args_tail=[], files=['bar', 'foo'], v=None, z=True) Namespace(args_tail=[], files=['bar', 'foo'], v=None, z=True) Namespace(args_tail=[], files=['a', 'b', 'c', 'd'], v=4, z=True) |
注意:不要试图将其与其他非标志参数(除了单个
我在argparse文档中没有看到任何明确的说明,说明它可以或不能排列。根据你自己的观察,如果排列失败,以及下面的文档引用,我将得出结论,这是不可能的。
已经有一个显式命名为"getopt"的模块:
Note The
getopt module is a parser for command line options whose API
is designed to be familiar to users of the Cgetopt() function. Users
who are unfamiliar with the Cgetopt() function or who would like to
write less code and get better help and error messages should consider
using theargparse module instead.
即使getopt的默认值不是permute,也有一个更明确定义的方法,名为
This function works like
getopt() , except that GNU style scanning mode
is used by default. This means that option and non-option arguments
may be intermixed.
在getopt文档中,通过包含以下内容,上述对argparse的引用被进一步夸大:
Note that an equivalent command line interface could be produced with
less code and more informative help and error messages by using the
argparse module:
再一次,没有明确的内容,但是在我看来,getopt和argparse之间有一个非常明显的区别,文档支持/提倡argparse。
下面是一个使用
1 2 3 4 5 6 7 8 | >>> args = 'file1 -z file2'.split() >>> args ['file1', '-z', 'file2'] >>> opts, args = getopt.gnu_getopt(args, 'z') >>> opts [('-z', '')] >>> args ['file1', 'file2'] |
编辑1:使用argparse进行自我排序
灵感来源于你链接到的"使用getopt"页面中的"permute"定义,
The default is to permute the contents of argv while scanning it so
that eventually all the non-options are at the end.
在将arg字符串传递给
1 2 3 4 5 | import argparse p = argparse.ArgumentParser(); p.add_argument('files',nargs='*',default=['-']); p.add_argument('-z',action='store_true') |
滚动您自己的:
1 2 3 4 5 6 7 8 9 10 | import re def permute(s, opts_ptn='-[abc]'): """Returns a permuted form of arg string s using a regular expression.""" opts = re.findall(opts_ptn, s) args = re.sub(opts_ptn, '', s) return '{} {}'.format(' '.join(opts), args).strip() >>> p.parse_args(permute('bar -z foo', '-[z]').split()) Namespace(files=['bar', 'foo'], z=True) |
利用getopt:
1 2 3 4 5 6 7 8 9 10 11 | import getopt def permute(s, opts_ptn='abc'): """Returns a permuted form of arg string s using `gnu_getop()'.""" opts, args = getopt.gnu_getopt(s.split(), opts_ptn) opts = ' '.join([''.join(x) for x in opts]) args = ' '.join(args) return '{} {}'.format(opts, args).strip() >>> p.parse_args(permute('bar -z foo', 'z').split()) Namespace(files=['bar', 'foo'], z=True) |