72

我编写了一个命令行实用程序,它使用 getopt 来解析命令行上给出的参数。我还想让文件名成为可选参数,例如在 grep、cut 等其他实用程序中。所以,我希望它具有以下用法

tool -d character -f integer [filename]

我该如何实施以下内容?

  • 如果给出了文件名,则从文件中读取。
  • 如果没有给出文件名,则从 STDIN 读取。
4

8 回答 8

86

fileinput模块可以做你想做的——假设非选项参数在里面args

import fileinput
for line in fileinput.input(args):
    print line

如果args为空fileinput.input(),则从标准输入读取;否则它会依次从每个文件中读取,方式类似于 Perl 的while(<>).

于 2009-11-16T21:35:51.240 回答
68

用最简单的话来说:

import sys
# parse command line
if file_name_given:
    inf = open(file_name_given)
else:
    inf = sys.stdin

此时您将使用inf从文件中读取。根据是否给出了文件名,这将从给定文件或标准输入中读取。

当你需要关闭文件时,你可以这样做:

if inf is not sys.stdin:
    inf.close()

但是,在大多数情况下,sys.stdin如果你完成了它,关闭它是无害的。

于 2009-11-16T21:40:54.830 回答
20

我喜欢使用上下文管理器的一般习惯用法,但是(太)微不足道的解决方案最终会sys.stdin在您退出with语句时关闭,我想避免这种情况。

借用这个答案,这是一个解决方法:

import sys
import contextlib

@contextlib.contextmanager
def _smart_open(filename, mode='Ur'):
    if filename == '-':
        if mode is None or mode == '' or 'r' in mode:
            fh = sys.stdin
        else:
            fh = sys.stdout
    else:
        fh = open(filename, mode)
    try:
        yield fh
    finally:
        if filename != '-':
            fh.close()
    
if __name__ == '__main__':
    args = sys.argv[1:]
    if args == []:
        args = ['-']
    for filearg in args:
        with _smart_open(filearg) as handle:
            do_stuff(handle)

我想你可以实现类似的东西,os.dup()但是我编写的代码变得更复杂、更神奇,而上面的代码有点笨拙但非常简单。

于 2015-04-23T12:53:48.360 回答
15

我更喜欢使用“-”作为您应该从标准输入读取的指示符,它更明确:

import sys
with open(sys.argv[1], 'r') if sys.argv[1] is not "-" else sys.stdin as f:
    pass # do something here
于 2015-01-25T21:00:51.830 回答
14

要使用 python 的with语句,可以使用以下代码:

import sys
with open(sys.argv[1], 'r') if len(sys.argv) > 1 else sys.stdin as f:
    # read data using f
    # ......
于 2013-11-06T01:49:06.857 回答
10

不是直接的答案,而是相关的。

通常,当您编写 python 脚本时,您可以使用该argparse包。如果是这种情况,您可以使用:

parser = argparse.ArgumentParser()
parser.add_argument('infile', nargs='?', type=argparse.FileType('r'), default=sys.stdin)

'?'。如果可能,将从命令行使用一个参数,并将其作为单个项目生成。如果不存在命令行参数,则将生成默认值。

在这里我们将默认设置为sys.stdin;

所以如果有一个文件,它将读取它,如果没有,它将从标准输入获取输入“注意:我们在上面的示例中使用了位置参数”

更多访问:https ://docs.python.org/2/library/argparse.html#nargs

于 2018-05-24T09:11:52.273 回答
3

切换到argparse(它也是标准库的一部分)并使用 argparse.FileType默认值 stdin:

import  argparse, sys

p = argparse.ArgumentParser()
p.add_argument('input', nargs='?',
  type=argparse.FileType(), default=sys.stdin)
args = p.parse_args()

print(args.input.readlines())

但是,这不会让您为标准输入指定编码和其他参数;如果你想这样做,你需要使参数成为非可选参数,并在作为参数给出FileType时使用 stdin 做它的事情:-

p.add_argument('input', type=FileType(encoding='UTF-8'))

注意后一种情况将不支持二进制模式 ( 'b') I/O。如果您需要它,您可以使用上面的默认参数技术,但提取二进制 I/O 对象,例如default=sys.stdout.buffer用于 stdout。但是,如果用户指定,这仍然会中断-。(使用-stdin/stdout 总是包裹在TextIOWrapper.)

如果您希望它与 . 一起使用-,或者在打开文件时需要提供任何其他参数,如果包装错误,您可以修复该参数:

p.add_argument('output', type=argparse.FileType('wb'))
args = p.parse_args()
if hasattr(args.output, 'buffer'):
    #   If the argument was '-', FileType('wb') ignores the 'b' when
    #   wrapping stdout. Fix that by grabbing the underlying binary writer.
    args.output = args.output.buffer

(帽子提示medhat提到add_argument()type参数。)

于 2020-09-03T03:40:58.117 回答
1

就像是:

if input_from_file:
    f = open(file_name, "rt")
else:
    f = sys.stdin
inL = f.readline()
while inL:
    print inL.rstrip()
    inL = f.readline()
于 2009-11-16T21:54:11.047 回答