为什么不是';Python argparse中的t fromfile前缀字符是否有效?
我试图在Python中使用argparse的fromfile prefix chars特性从一个文件加载所有命令行参数,但它一直抱怨我没有指定一些参数 守则:为什么不是';Python argparse中的t fromfile前缀字符是否有效?,python,python-2.7,argparse,Python,Python 2.7,Argparse,我试图在Python中使用argparse的fromfile prefix chars特性从一个文件加载所有命令行参数,但它一直抱怨我没有指定一些参数 守则: import argparse def go(): parser = argparse.ArgumentParser(fromfile_prefix_chars='@') parser.add_argument("--option1") parser.add_argument("--option2", type=int
import argparse
def go():
parser = argparse.ArgumentParser(fromfile_prefix_chars='@')
parser.add_argument("--option1")
parser.add_argument("--option2", type=int, required=True)
args = parser.parse_args()
if __name__ == "__main__":
go()
参数文件:
--option1 foo
--option2 1234
命令行和输出:
$ python testargparse.py @testargs
usage: testargparse.py [-h] [--option1 OPTION1] --option2 OPTION2
testargparse.py: error: argument --option2 is required
您可以看到我在文件中提供了所需的参数,但argparse没有看到它。问题是,在文件中指定时,每个参数与选项名之间必须有一个“=”。当从命令行运行argparse时(其中空格或=可以),argparse在该格式上更加灵活,但从文件运行时,它必须有一个“=” 因此,工作参数文件应该是:
--option1=foo
--option2=1234
还有一点需要注意,请确保行的末尾没有多余的空格,或者在argparse读取文件时,空格将包含在选项中。来自文档:
def sh_split(arg_line):
for arg in shlex.split(arg_line):
yield arg
parser.convert_arg_line_to_args = sh_split
默认情况下,从文件读取的参数必须为每行一个。。。和被视为与命令行上的原始文件引用参数位于同一位置。因此,在上面的示例中,表达式['-f',foo','@args.txt']被认为等同于表达式['-f',foo','-f',bar']
在示例中:
fp.write('-f\nbar')
因此,该文件包含:
-f
bar
换句话说,每个文件行对应于命令行中的一个“字”(空格分隔)<代码>--option1=foo是一个单词--option1foo
的解释就像它在命令行中被引用一样,例如prog.py'--option1foo'--option21234'
具有一个自定义函数,该函数将分割空间上的线。如果您想继续使用参数文件,请尝试使用它
@toes
建议使用shlex
解析文件shlex
有一个很好的特性,它去掉了不必要的引号
shlex
可用于分割文件的各行
def sh_split(arg_line):
for arg in shlex.split(arg_line):
yield arg
parser.convert_arg_line_to_args = sh_split
或者它可以替换整个@file read方法(\u read\u args\u from\u files
)-这应该与@toes
应答功能相同,只是@file
字符串可以位于命令行中的任何位置(甚至可以重复)
显然,清洁生产版本会在ArgumentParser
子类中修改这些方法
# encoding: utf-8
import imp
import argparse
class LoadConfigAction(argparse._StoreAction):
NIL = object()
def __init__(self, option_strings, dest, **kwargs):
super(self.__class__, self).__init__(option_strings, dest)
self.help = "Load configuration from file"
def __call__(self, parser, namespace, values, option_string=None):
super(LoadConfigAction, self).__call__(parser, namespace, values, option_string)
config = imp.load_source('config', values)
for key in (set(map(lambda x: x.dest, parser._actions)) & set(dir(config))):
setattr(namespace, key, getattr(config, key))
用法
示例配置(real是python文件)
我认为有一个更好的答案:使用shlex
if sys.argv[1].startswith('@'):
args = parser.parse_args( shlex.split(open(sys.argv[1][1:]).read()) )
else:
args = parser.parse_args()
这允许您以更自然的方式指定文件中的参数,例如,它允许使用空格或等号在单行上指定参数,如:
arg1
arg2
--opt1 'foo'
--opt2='bar'
shlex.split按照您的预期拆分此文件:
['arg1', 'arg2', '--opt1', 'foo', '--opt2=bar']
这个方法唯一没有的是它希望@file.txt作为第一个参数。我很欣赏这个答案中的细节,但我认为对于只想让参数文件工作的用户来说,这太过分了。我认为应该强调的是,最简单的解决方案是在文件中添加一个“=”。如果你把我的答案和你的答案结合起来(前面有简单的解决方案),我认为会更好,并且会接受它。文档中可能存在的一个问题是“论点”的含义含糊不清。您可能一直认为
--option1foo
是一个参数,或者是可选+参数。但是doc真正的意思是参数字符串
,即argv
所划分的字符串。实际上,在任何真正的解析开始之前,文件中的字符串被插入到argv
中。您可能可以将此shlex.split
与我描述的convert_arg_line_to_args
方法结合使用。这将拼接出现@
字符串的值。
if sys.argv[1].startswith('@'):
args = parser.parse_args( shlex.split(open(sys.argv[1][1:]).read()) )
else:
args = parser.parse_args()
arg1
arg2
--opt1 'foo'
--opt2='bar'
['arg1', 'arg2', '--opt1', 'foo', '--opt2=bar']