GNU getopt和使用它的命令行工具允许交叉选项和参数,称为置换选项(参见http://www.gnu.org/software/libc/manual/html_node/Using-Getopt.html#Using-Getopt)。 Perl的Getopt :: Long模块也支持这个(使用qw(:config gnu_getopt))。 argparse似乎不支持(甚至提及)置换选项。
有许多与arg / opt命令相关的SO问题,但似乎没有人回答这个问题:argparse可以像getopt那样置换参数顺序吗?
用例是一个典型的命令行签名,如GNU sort:
sort [opts] [files]
其中1)选项和文件被置换,2)文件列表可能包含零个或多个参数。
例如:
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 ...]]
我试过了:
我想实现一些接近上面GNU排序原型的东西。我对可以为每个文件指定的标志不感兴趣(例如,-f file1 -f file2)。
答案 0 :(得分:4)
这是一个快速解决方案,一次解码参数列表一(选项,位置参数)对。
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()))
输出:
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)
注意:不要尝试将其与其他非标志参数一起使用(除了单个nargs='*'
参数和args_tail
参数)。解析器将不知道parse_args
的先前调用,因此它将为这些非标志参数存储错误的值。作为解决方法,您可以在使用nargs='*'
后手动解析interleaved_parse
参数。
答案 1 :(得分:3)
我在argparse文档中没有看到任何明确的说明它可以或不可以置换。根据您自己的观察结果,排列失败,以及以下文档引用,我将得出结论无法完成。
已经有一个明确命名为“getopt”的模块:
注意
getopt
模块是API API命令行选项的解析器 被设计为熟悉Cgetopt()
函数的用户。用户 谁不熟悉Cgetopt()
函数或谁想要 编写更少的代码并获得更好的帮助和错误消息应该考虑 改为使用argparse
模块。
即使是getopt的默认值也没有置换,有一个名为gnu_getopt()
的更明确定义的方法:
此功能与
getopt()
类似,但GNU样式扫描模式除外 默认情况下使用。这意味着选项和非选项参数 可以混合。
在getopt文档中,通过包含以下内容进一步夸大了对argparse的上述引用:
请注意,可以生成等效的命令行界面 通过使用更少的代码和更多信息的帮助和错误消息
argparse
模块:
同样,没有什么是明确的,但是,对我来说,getopt和argparse之间正在形成一个非常明显的鸿沟,文档偏向/倡导argparse。
以下是使用gnu_getop()
的示例,它可以满足您的-z [file [file]]
测试:
>>> 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”定义的启发,
默认情况下,扫描时会置换argv的内容 最终所有的非选项都在最后。
如何在将arg字符串传递给parse_args()
之前置换它?
import argparse
p = argparse.ArgumentParser();
p.add_argument('files',nargs='*',default=['-']);
p.add_argument('-z',action='store_true')
滚动自己:
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:
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)