使用optparse在Python中将stdin和stdout重定向到文件的一致方法

14

我有十几个程序可以通过标准输入或选项接受输入,我希望以类似的方式实现相同的功能来处理输出。

optparse 代码看起来像这样:

parser.add_option('-f', '--file',
       default='-',
       help='Specifies the input file.  The default is stdin.')
parser.add_option('-o', '--output',
       default='-',
       help='Specifies the output file.  The default is stdout.')

其他相关代码如下:

if opts.filename == '-':
    infile = sys.stdin
else:
    infile = open(opts.filename, "r")

if opts.output == '-':
    outfile = sys.stdout
else:
    outfile = open(opts.output, "w")

这段代码工作得很好,我喜欢它的简洁性 - 但是我没有找到任何人使用默认值“-”来表示标准输出的参考。这是一个好的一致性解决方案还是我忽略了更好或更符合预期的东西?


小澄清 - 我曾经看到过一些帖子,建议在optparse/argparse中使用破折号作为指示符,表示程序应从stdin读取。但我没有看到过这种用法适用于stdout。我不知道背后的原因是什么 - 这听起来熟悉吗?有什么见解吗?否则,Adam Wagner的建议看起来稍微更简单和易读一些。 - KenFar
2个回答

15

你可以使用fileinput模块来处理输入文件。它遵循常见的输入文件约定:如果没有给出文件名或文件名为“-”,则读取标准输入,否则从命令行给出的文件中读取。

不需要使用-f--file选项。如果程序始终需要输入文件,则这不是一个选项。

-o--output用于指定各种程序中的输出文件名

optparse

#!/usr/bin/env python
import fileinput
import sys
from optparse import OptionParser

parser = OptionParser()
parser.add_option('-o', '--output',
    help='Specifies the output file.  The default is stdout.')
options, files = parser.parse_args()
if options.output and options.output != '-':
   sys.stdout = open(options.output, 'w')

for line in fileinput.input(files):
    process(line)

argparse

argparse 模块允许您明确指定文件作为参数:

#!/usr/bin/env python
import fileinput
import sys
from argparse import ArgumentParser

parser = ArgumentParser()
parser.add_argument('files', nargs='*', help='specify input files')
group = parser.add_mutually_exclusive_group()
group.add_argument('-o', '--output', 
    help='specify the output file.  The default is stdout')
group.add_argument('-i', '--inplace', action='store_true',
    help='modify files inplace')
args = parser.parse_args()

if args.output and args.output != '-':
   sys.stdout = open(args.output, 'w')

for line in fileinput.input(args.files, inplace=args.inplace):
    process(line)

注意:我在第二个例子中添加了--inplace选项:

$ python util-argparse.py --help
usage: util-argparse.py [-h] [-o OUTPUT | -i] [files [files ...]]

positional arguments:
  files                 specify input files

optional arguments:
  -h, --help            show this help message and exit
  -o OUTPUT, --output OUTPUT
                        specify the output file. The default is stdout
  -i, --inplace         modify files inplace

文件输入(fileinput)和 CSV 模块看起来不太兼容,所以我没有在这些程序中使用文件输入。我的假设错了吗? - KenFar
@KenFar:你所说的“兼容”是什么意思?csv.reader(fileinput.input())按预期工作。 - jfs
哎呀,我的错。看起来这是最佳解决方案,因为它还能接受多个输入文件。谢谢。 - KenFar
@J.F. Sebastian,你好,我发现在argparse的例子中,当输出是一个文件时,代码并没有显式调用close(),这样可以吗? - Bily
是的,你想要保持标准输出一直开启,直到你的脚本结束。 - jfs

12
如果您可以使用argparse(即Python 2.7+),它内置了您想要的支持:直接从argparse docFileType 工厂创建可以传递给 ArgumentParser.add_argument() 的对象。作为其类型的 FileType 对象的参数将打开命令行参数[...]。 FileType 对象了解伪参数“ - ”,并自动将其转换为可读 FileType 对象的sys.stdin和可写 FileType 对象的sys.stdout。因此,我的建议是简单地使用。
import sys
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('file', type=argparse.FileType('r'),
    help="Specifies the input file")
parser.add_argument('output', type=argparse.FileType('w'),
    help="Specifies the output file")
args = parser.parse_args(sys.argv[1:])

# Here you can use your files
text = args.file.read()
args.output.write(text)
# … and so on

然后你可以做

> python spam.py file output 

要从file中读取并输出到output,或者

> echo "Ni!" | python spam.py - output  

读取"Ni!"并输出到output,或者

> python spam.py file -

这很好,因为在相关流中使用-是许多程序使用的约定。如果你想指出它,请将其添加到帮助字符串中。

  parser.add_argument('file', type=argparse.FileType('r'),
    help="Specifies the input file, '-' for standard input")

作为参考,使用说明将会是:

> python spam.py -h
usage: [-h] file output

positional arguments:
  file        Specifies the input file, '-' for standard input
  output      Specifies the output file, '-' for standard output

optional arguments:
  -h, --help  show this help message and exit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接