像典型的过滤程序一样解析cmd args

时间:2015-07-14 00:28:03

标签: python python-2.7 argparse

我花了几个小时阅读关于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:])

谢谢!

2 个答案:

答案 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:]的帮助下,您可以轻松地在inlistoutfile之间轻松分割argparse