为什么不是';Python argparse中的t fromfile前缀字符是否有效?

为什么不是';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

我试图在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, 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']