如何传递文件中包含的命令行参数并保留该文件的名称?
How can I pass command line arguments contained in a file and retain the name of that file?
我有一个使用命令行参数的脚本,我想实现两个参数传递方案,即:
- 在命令行输入参数。
- 将参数列表存储在文件中,并通过命令行将此文件的名称传递给程序。
为此,我将参数 fromfile_prefix_chars
传递给 ArgumentParser
构造函数。
script.py
from argparse import ArgumentParser
parser = ArgumentParser(fromfile_prefix_chars='@')
parser.add_argument('filename', nargs='?')
parser.add_argument('--foo', nargs='?', default=1)
parser.add_argument('--bar', nargs='?', default=1)
args = parser.parse_args()
print(args)
args.txt
--foo
2
--bar
2
示例用例
$ python script.py --foo 3
Namespace(bar=1, filename=None, foo='3')
$ python script.py @args.txt --foo 3
Namespace(bar='2', filename=None, foo='3')
我原以为 args.filename
会保留文件名,但令人惊讶的是它的值是 None
。我知道我可以通过一些处理从 sys.argv
获取文件名。是否有更简洁的方法(最好是基于 argparse
的方法)来获取参数文件的名称?
根据我的测试,使用 fromfile_prefix_chars
意味着 argparse 实际上不会将参数传递给您的程序。相反,argparse
会看到 @args.txt
,拦截它,从中读取,并将不带 @args.txt
的参数传递给您的程序。这大概是因为大多数人并不真正需要文件名,只需要其中的参数,所以 argparse
省去了您创建另一个参数来存储不需要的东西的麻烦。
不幸的是,所有参数都作为局部变量存储在 argparse.py
中,因此我们无法访问它们。我想你可以覆盖一些 argparse 的功能。请记住,这是一个可怕的、令人作呕的、老掉牙的解决方案,我觉得解析 sys.argv
好 100%。
from argparse import ArgumentParser
# Most of the following is copied from argparse.py
def customReadArgs(self, arg_strings):
# expand arguments referencing files
new_arg_strings = []
for arg_string in arg_strings:
# for regular arguments, just add them back into the list
if not arg_string or arg_string[0] not in self.fromfile_prefix_chars:
new_arg_strings.append(arg_string)
# replace arguments referencing files with the file content
else:
try:
fn = arg_string[1:]
with open(fn) as args_file:
# What was changed: before was []
arg_strings = [fn]
for arg_line in args_file.read().splitlines():
for arg in self.convert_arg_line_to_args(arg_line):
arg_strings.append(arg)
arg_strings = self._read_args_from_files(arg_strings)
new_arg_strings.extend(arg_strings)
except OSError:
err = _sys.exc_info()[1]
self.error(str(err))
# return the modified argument list
return new_arg_strings
ArgumentParser._read_args_from_files = customReadArgs
parser = ArgumentParser(fromfile_prefix_chars='@')
parser.add_argument('filename', nargs='?')
parser.add_argument('--foo', nargs='?', default=1)
parser.add_argument('--bar', nargs='?', default=1)
args = parser.parse_args()
print(args)
您的 script.py
,加上文件。我已将文件名添加到文件本身。
args.txt
args.txt
--foo
2
--bar
2
测试:
1803:~/mypy$ python3 stack56811067.py --foo 3
Namespace(bar=1, filename=None, foo='3')
1553:~/mypy$ python3 stack56811067.py @args.txt --foo 3
Namespace(bar='2', filename='args.txt', foo='3')
仅作记录,这是我想出的一个快速而肮脏的解决方案。它基本上包括创建 parser
的副本并将其 from_file_prefix_chars
属性设置为 None
:
import copy
parser_dupe = copy.copy(parser)
parser_dupe.fromfile_prefix_chars = None
args_raw = parser_dupe.parse_args()
if args_raw.filename:
args.filename = args_raw.filename[1:]
我有一个使用命令行参数的脚本,我想实现两个参数传递方案,即:
- 在命令行输入参数。
- 将参数列表存储在文件中,并通过命令行将此文件的名称传递给程序。
为此,我将参数 fromfile_prefix_chars
传递给 ArgumentParser
构造函数。
script.py
from argparse import ArgumentParser
parser = ArgumentParser(fromfile_prefix_chars='@')
parser.add_argument('filename', nargs='?')
parser.add_argument('--foo', nargs='?', default=1)
parser.add_argument('--bar', nargs='?', default=1)
args = parser.parse_args()
print(args)
args.txt
--foo
2
--bar
2
示例用例
$ python script.py --foo 3
Namespace(bar=1, filename=None, foo='3')
$ python script.py @args.txt --foo 3
Namespace(bar='2', filename=None, foo='3')
我原以为 args.filename
会保留文件名,但令人惊讶的是它的值是 None
。我知道我可以通过一些处理从 sys.argv
获取文件名。是否有更简洁的方法(最好是基于 argparse
的方法)来获取参数文件的名称?
根据我的测试,使用 fromfile_prefix_chars
意味着 argparse 实际上不会将参数传递给您的程序。相反,argparse
会看到 @args.txt
,拦截它,从中读取,并将不带 @args.txt
的参数传递给您的程序。这大概是因为大多数人并不真正需要文件名,只需要其中的参数,所以 argparse
省去了您创建另一个参数来存储不需要的东西的麻烦。
不幸的是,所有参数都作为局部变量存储在 argparse.py
中,因此我们无法访问它们。我想你可以覆盖一些 argparse 的功能。请记住,这是一个可怕的、令人作呕的、老掉牙的解决方案,我觉得解析 sys.argv
好 100%。
from argparse import ArgumentParser
# Most of the following is copied from argparse.py
def customReadArgs(self, arg_strings):
# expand arguments referencing files
new_arg_strings = []
for arg_string in arg_strings:
# for regular arguments, just add them back into the list
if not arg_string or arg_string[0] not in self.fromfile_prefix_chars:
new_arg_strings.append(arg_string)
# replace arguments referencing files with the file content
else:
try:
fn = arg_string[1:]
with open(fn) as args_file:
# What was changed: before was []
arg_strings = [fn]
for arg_line in args_file.read().splitlines():
for arg in self.convert_arg_line_to_args(arg_line):
arg_strings.append(arg)
arg_strings = self._read_args_from_files(arg_strings)
new_arg_strings.extend(arg_strings)
except OSError:
err = _sys.exc_info()[1]
self.error(str(err))
# return the modified argument list
return new_arg_strings
ArgumentParser._read_args_from_files = customReadArgs
parser = ArgumentParser(fromfile_prefix_chars='@')
parser.add_argument('filename', nargs='?')
parser.add_argument('--foo', nargs='?', default=1)
parser.add_argument('--bar', nargs='?', default=1)
args = parser.parse_args()
print(args)
您的 script.py
,加上文件。我已将文件名添加到文件本身。
args.txt
args.txt
--foo
2
--bar
2
测试:
1803:~/mypy$ python3 stack56811067.py --foo 3
Namespace(bar=1, filename=None, foo='3')
1553:~/mypy$ python3 stack56811067.py @args.txt --foo 3
Namespace(bar='2', filename='args.txt', foo='3')
仅作记录,这是我想出的一个快速而肮脏的解决方案。它基本上包括创建 parser
的副本并将其 from_file_prefix_chars
属性设置为 None
:
import copy
parser_dupe = copy.copy(parser)
parser_dupe.fromfile_prefix_chars = None
args_raw = parser_dupe.parse_args()
if args_raw.filename:
args.filename = args_raw.filename[1:]