GNU getopt 和使用它的命令行工具允许选项和参数交错,称为排列选项(请参阅 http://www.gnu.org/software/libc/manual/html_node/Using-Getopt.html #使用-Getopt)。 Perl 的 Getopt::Long 模块也支持此功能(使用 qw(:config gnu_getopt))。 argparse 似乎不支持(甚至不提及)排列选项。
有很多与 arg/opt 顺序相关的问题,但似乎没有人回答这个问题:Can argparse be made to permute argument order like 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)。
这是一个快速解决方案,一次解码一对参数列表(选项、位置参数)。
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
参数。
我在 argparse 文档中没有看到任何明确的内容表明它可以或不能排列。根据您自己的观察,排列失败的地方以及以下文档引用,我将得出结论,这是无法完成的。
已经有一个显式命名为“getopt”的模块:
注意
模块是命令行选项的解析器,其 API 旨在让 Cgetopt
函数的用户熟悉。用户 不熟悉 Cgetopt()
函数或想要了解的人 编写更少的代码并获得更好的帮助和错误消息应该考虑 使用getopt()
模块代替。argparse
即使 getopt 的默认值不会排列,也有一个更明确定义的方法,名为
gnu_getopt()
:
此功能的工作方式与
类似,只不过是 GNU 风格的扫描模式 默认使用。这意味着选项和非选项参数 可能会混合。getopt()
在 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”页面中“排列”定义的启发,
默认是在扫描时排列 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)
Python 3.7通过“parse_intermixed_args”添加了对此的支持 - 请参阅https://docs.python.org/3.7/library/argparse.html#argparse.ArgumentParser.parse_intermixed_args
我没有找到像在终端中那样向
argparse
提供多个组合参数的方法。例如 ls -la
或 ps -aux
。但是,我尝试了一种与参数的终端用法有些相似的方法。意思是综合论证。
示例代码:test.py
import argparse
from itertools import permutations
parser = argparse.ArgumentParser()
parser.add_argument('--image-dir', '-i', type=str, help='path to directory where the image files are presented', default=None)
parser.add_argument('--text-dir', '-t', type=str, help='path to directory where the text files are presented', default=None)
parser.add_argument('--json-dir', '-j', type=str, help='path to directory to save the generated json files', default=None)
parser.add_argument(*list(map(lambda x: '-' +''.join(x), permutations('it'))), dest='image_text_dir',type=str, help='combined path of image and text', default=None)
parser.add_argument(*list(map(lambda x: '-' +''.join(x), permutations('jt'))), dest='json_text_dir',type=str, help='combined path of json and text', default=None)
parser.add_argument(*list(map(lambda x: '-' +''.join(x), permutations('ji'))), dest='json_image_dir',type=str, help='combined path of image and json', default=None)
parser.add_argument(*list(map(lambda x: '-' +''.join(x), permutations('itj'))), dest='image_text_json_dir',type=str, help='combined path of image, text, and json', default=None)
parser.add_argument('--verbose', '-v', action='store_true')
args = parser.parse_args()
image_dir = vars(args)['image_dir']
txt_dir = vars(args)['text_dir']
json_dir = vars(args)['json_dir']
verbose = vars(args)['verbose']
image_text_dir = vars(args)['image_text_dir']
json_text_dir = vars(args)['json_text_dir']
json_image_dir = vars(args)['json_image_dir']
image_text_json_dir = vars(args)['image_text_json_dir']
if image_text_dir:
image_dir = txt_dir = image_text_dir
if json_text_dir:
json_dir = txt_dir = json_text_dir
if json_image_dir:
json_dir = image_dir = json_image_dir
if image_text_json_dir:
image_dir = txt_dir = json_dir = image_text_json_dir
print(f'Image {image_dir}, Text {txt_dir}, and json {json_dir}')
现在在终端中,您可以运行该文件:
name@hostname:~$ py test.py -ijt ./path/to/file
Image ./path/to/file, Text ./path/to/file, and json ./path/to/file
或者,
name@hostname:~$ py test.py -ij ./path/to/file
Image ./path/to/file, Text None, and json ./path/to/file
我希望这就是您正在寻找的。