简体   繁体   English

从文件或 STDIN 读取

[英]Read from File, or STDIN

I've written a command line utility that uses getopt for parsing arguments given on the command line.我编写了一个命令行实用程序,它使用 getopt 来解析命令行上给出的 arguments。 I would also like to have a filename be an optional argument, such as it is in other utilities like grep, cut etc. So, I would like it to have the following usage我还想让文件名成为可选参数,例如在 grep、cut 等其他实用程序中。所以,我希望它具有以下用法

tool -d character -f integer [filename]

How can I implement the following?我该如何实施以下内容?

  • if a filename is given, read from the file.如果给出了文件名,则从文件中读取。
  • if a filename is not given, read from STDIN.如果没有给出文件名,则从 STDIN 读取。

The fileinput module may do what you want - assuming the non-option arguments are in args then: fileinput模块可以做你想做的 - 假设非选项参数在args那么:

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

If args is empty then fileinput.input() will read from stdin;如果args为空,则fileinput.input()将从标准输入读取; otherwise it reads from each file in turn, in a similar manner to Perl's while(<>) .否则它会依次从每个文件中读取,类似于 Perl 的while(<>)

In the simplest terms:用最简单的话来说:

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

At this point you would use inf to read from the file.此时,您将使用inf从文件中读取。 Depending on whether a filename was given, this would read from the given file or from stdin.根据是否给定文件名,这将从给定的文件或标准输入读取。

When you need to close the file, you can do this:当您需要关闭文件时,您可以这样做:

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

However, in most cases it will be harmless to close sys.stdin if you're done with it.然而,在大多数情况下,如果你完成了关闭sys.stdin是无害的。

I like the general idiom of using a context manager, but the (too) trivial solution ends up closing sys.stdin when you are out of the with statement, which I want to avoid.我喜欢使用上下文管理器的一般习惯用法,但是当您退出with语句时,(太)琐碎的解决方案最终会关闭sys.stdin ,我想避免这种情况。

Borrowing from this answer , here is a workaround:借用这个答案,这是一个解决方法:

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)

I suppose you could achieve something similar with os.dup() but the code I cooked up to do that turned out to be more complex and more magical, whereas the above is somewhat clunky but very straightforward.我想你可以os.dup()实现类似的东西,但我编写的代码变得更加复杂和神奇,而上面的代码有点笨拙但非常简单。

I prefer to use "-" as an indicator that you should read from stdin, it's more explicit:我更喜欢使用“-”作为您应该从标准输入读取的指示符,它更明确:

import sys
with open(sys.argv[1], 'r') if sys.argv[1] is not "-" else sys.stdin as f:
    pass # do something here

To make use of python's with statement, one can use the following code:要使用 python 的with语句,可以使用以下代码:

import sys
with open(sys.argv[1], 'r') if len(sys.argv) > 1 else sys.stdin as f:
    # read data using f
    # ......

Not a direct answer but related.不是直接的答案,而是相关的。

Normally when you write a python script you could use the argparse package.通常,当您编写 python 脚本时,您可以使用argparse包。 If this is the case you can use:如果是这种情况,您可以使用:

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

'?'. '?'。 One argument will be consumed from the command line if possible, and produced as a single item.如果可能,将从命令行使用一个参数,并作为单个项目生成。 If no command-line argument is present, the value from default will be produced.如果不存在命令行参数,则将生成默认值。

and here we set default to sys.stdin ;在这里我们将默认设置为sys.stdin

so If there is a file it will read it , and if not it will take the input from stdin "Note: that we are using positional argument in the example above"所以如果有一个文件,它将读取它,如果没有,它将从标准输入中获取输入“注意:我们在上面的例子中使用了位置参数”

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

Switch to argparse (it's also part of the standard library) and use an argparse.FileType with a default value of stdin:切换到argparse (它也是标准库的一部分)并使用默认值为 stdin 的argparse.FileType

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())

This will not let you specify encoding and other parameters for stdin, however;但是,这不会让您为 stdin 指定编码和其他参数; if you want to do that you need to make the argument non-optional and let FileType do its thing with stdin when - is given as an argument:如果你想这样做,你需要使参数成为非可选的,并让FileType-作为参数给出时使用 stdin 做它的事情:

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

Take heed that this latter case will not honour binary mode ( 'b' ) I/O.请注意,后一种情况将不支持二进制模式 ( 'b' ) I/O。 If you need only that, you can use the default argument technique above, but extract the binary I/O object, eg, default=sys.stdout.buffer for stdout.如果您需要它,您可以使用上面的默认参数技术,但提取二进制 I/O 对象,例如,对于 stdout, default=sys.stdout.buffer However, this will still break if the user specifies - anyway.但是,如果用户指定-无论如何,这仍然会中断。 (With - stdin/stdout is always wrapped in a TextIOWrapper .) (使用- stdin/stdout 始终包装在TextIOWrapper 。)

If you want it to work with - , or have any other arguments you need to provide when opening the file, you can fix the argument if it got wrapped wrong:如果您希望它与- ,或者在打开文件时需要提供任何其他参数,则可以在包装错误时修复该参数:

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

(Hat tip to medhat for mentioning add_argument() 's type parameter.) (提示medhat提及add_argument()type参数。)

Something like:就像是:

if input_from_file:
    f = open(file_name, "rt")
else:
    f = sys.stdin
inL = f.readline()
while inL:
    print inL.rstrip()
    inL = f.readline()

A KISS solution is: KISS 解决方案是:

if file == "-":
    content = sys.stdin.read()
else:
    with open(file) as f:
        content = f.read()
print(content)   # Or whatever you want to do with the content of the file.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM