我花了几个小时阅读关于argparse的教程并设法学习使用普通参数。 official documentation对我来说不太可读。我是Python的新手。我正在尝试编写一个可以通过以下方式调用的程序:
cat inFile | program [options] > outFile
- 如果未指定inFile或outfile,则从stdin读取并输出到stdout。
program [options] inFile outFile
program [options] inFile > outFile
- 如果只指定了一个文件,则输入和输出应该转到stdout。
cat inFile | program [options] - outFile
- 如果用' - '代替inFlie从stdin读取。
program [options] /path/to/folder outFile
- 处理来自/path/to/folder
及其子目录的所有文件。
我希望它在GNU / Linux下表现得像普通的cli程序。
如果可以调用该程序也会很好:
program [options] inFile0 inFile1 ... inFileN outFile
- 第一个路径/文件始终被解释为输入,最后一个始终被解释为输出。任何其他的解释为输入。
我可能会编写可以实现此目的的脏代码,但这将被使用,所以有人最终会维护它(他会知道我住的地方......)。
非常感谢任何帮助/建议。
结合答案和来自互联网的更多知识我已经设法写了这个(它不接受多个输入,但这已经足够了):
import sys, argparse, os.path, glob
def inputFile(path):
if path == "-":
return [sys.stdin]
elif os.path.exists(path):
if os.path.isfile(path):
return [path]
else:
return [y for x in os.walk(path) for y in glob.glob(os.path.join(x[0], '*.dat'))]
else:
exit(2)
def main(argv):
cmdArgsParser = argparse.ArgumentParser()
cmdArgsParser.add_argument('inFile', nargs='?', default='-', type=inputFile)
cmdArgsParser.add_argument('outFile', nargs='?', default='-', type=argparse.FileType('w'))
cmdArgs = cmdArgsParser.parse_args()
print cmdArgs.inFile
print cmdArgs.outFile
if __name__ == "__main__":
main(sys.argv[1:])
谢谢!
答案 0 :(得分:2)
您需要一个位置参数(名称不以短划线开头),可选参数(nargs='?'
),默认参数(default='-'
)。此外,argparse.FileType
是一个方便工厂,如果sys.stdin
通过,则返回sys.stdout
或-
。(具体取决于模式)。
所有在一起:
#!/usr/bin/env python
import argparse
# default argument is sys.argv[0]
parser = argparse.ArgumentParser('foo')
parser.add_argument('in_file', nargs='?', default='-', type=argparse.FileType('r'))
parser.add_argument('out_file', nargs='?', default='-', type=argparse.FileType('w'))
def main():
# default argument is is sys.argv[1:]
args = parser.parse_args(['bar', 'baz'])
print(args)
args = parser.parse_args(['bar', '-'])
print(args)
args = parser.parse_args(['bar'])
print(args)
args = parser.parse_args(['-', 'baz'])
print(args)
args = parser.parse_args(['-', '-'])
print(args)
args = parser.parse_args(['-'])
print(args)
args = parser.parse_args([])
print(args)
if __name__ == '__main__':
main()
答案 1 :(得分:0)
我会给你一个开始剧本。它使用optionals
而不是positionals
。并且只有一个输入文件。但是应该尝试一下你能做些什么。
import argparse
parser = argparse.ArgumentParser()
inarg = parser.add_argument('-i','--infile', type=argparse.FileType('r'), default='-')
outarg = parser.add_argument('-o','--outfile', type=argparse.FileType('w'), default='-')
args = parser.parse_args()
print(args)
cnt = 0
for line in args.infile:
print(cnt, line)
args.outfile.write(line)
cnt += 1
当没有参数调用时,它只是回显您的输入(在^ D之后)。我有点困扰,直到我发出另一个^ D才会退出。
FileType
很方便,但有主要错误 - 它会打开文件,但你必须自己关闭它们,或者让Python在退出时这样做。还有一个复杂的问题,你不想关闭stdin / out。
最好的argparse
问题包括基本脚本以及有关如何纠正或改进它的具体问题。你的规格相当清楚。但如果你给我们更多的工作,那就太好了。
要处理子目录选项,我会跳过FileType
位。使用argparse
获取2个字符串列表(或列表和名称),然后执行必要的chgdir
和/ glob
来查找和迭代文件。不要指望argparse
做实际的工作。用它来解析命令行字符串。这里是这样一个脚本的草图,留下大部分细节供你填写。
import argparse
import os
import sys # of stdin/out
....
def open_output(outfile):
# function to open a file for writing
# should handle '-'
# return a file object
def glob_dir(adir):
# function to glob a dir
# return a list of files ready to open
def open_forread(afilename):
# function to open file for reading
# be sensitive to '-'
def walkdirs(alist):
outlist = []
for name in alist:
if <name is file>;
outlist.append(name)
else <name is a dir>:
glist = glob(dir)
outlist.extend(glist)
else:
<error>
return outlist
def cat(infile, outfile):
<do your thing here>
def main(args):
# handle args options
filelist = walkdirs(args.inlist)
fout = open_outdir(args.outfile)
for name in filelist:
fin = open_forread(name)
cat(fin,fout)
if <fin not stdin>: fin.close()
if <fout not stdout>: fout.close()
if '__name__' == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('inlist', nargs='*')
parser.add_argument('outfile')
# add options
args = parser.parse_args()
main(args)
此处parser
要求您提供outfile
名称,即使它是' - '。我可以定义它的nargs='?'
以使其成为可选项。但这并不适合'inlist`'*'。
考虑
myprog one two three
那是
namespace(inlist=['one','two','three'], outfile=default)
或
namespace(inlist=['one','two'], outfile='three')
同时使用*
和?
位置,最后一个字符串的标识不明确 - 它是inlist
的最后一个条目,还是outfile
的可选条目? argparse
选择前者,并且永远不会将值分配给outfile
。
使用--infile
,--outfile
定义,这些字符串的分配是明确的。
从某种意义上讲,这个问题对于argparse
而言过于复杂 - 它没有什么可以处理像目录这样的东西。从另一个意义上讲,它太简单了。在sys.argv[1:]
的帮助下,您可以轻松地在inlist
和outfile
之间轻松分割argparse
。