从文件或STDIN中读取

Rya*_*rio 59 python command-line stdin arguments file

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

tool -d character -f integer [filename]
Run Code Online (Sandbox Code Playgroud)

我该如何实施以下内容?

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

Sim*_*onJ 74

的FileInput模块可以做你想做的-假设非选项参数是args那么:

import fileinput
for line in fileinput.input(args):
    print line
Run Code Online (Sandbox Code Playgroud)

如果args为空fileinput.input()则将从stdin读取; 否则它会以与Perl类似的方式依次从每个文件中读取while(<>).

  • fileinput 是一个奇怪而烦人的 API,它迫使您在命令行上使用标记的参数。 (2认同)
  • @ctpenrose 这不是文件输入设计错误:将作为输入文件名称的参数与其他参数区分开来是问题域固有的问题。Fileinput(尤其是使用 argparse)简化了执行此操作的通用模式的使用,您可以选择使用或不使用,但如果有其他区分方式,您可以发送一个 sys.argv 切片(或不同的名称数组)到 fileinput.input() - 当您显式传递数组时,您不必放入假的 sys.argv[0] 。 (2认同)

Gre*_*ill 59

用最简单的术语来说:

import sys
# parse command line
if file_name_given:
    inf = open(file_name_given)
else:
    inf = sys.stdin
Run Code Online (Sandbox Code Playgroud)

此时您将使用inf从文件中读取.根据是否给出文件名,这将从给定文件或stdin读取.

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

if inf is not sys.stdin:
    inf.close()
Run Code Online (Sandbox Code Playgroud)

但是,在大多数情况下,sys.stdin如果你已经完成它将是无害的.

  • 我找到了解决这个问题的另一种方法,我在这里写了博客http://dfourtheye.blogspot.in/2013/05/python-equivalent-of-cs-freopen.html并添加了这个问题的答案. (2认同)

tri*_*eee 16

我喜欢使用上下文管理器的一般习惯用法,但是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 is not '-':
            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)
Run Code Online (Sandbox Code Playgroud)

我想你可以实现类似的东西,os.dup()但我做的代码变得更复杂,更神奇,而上面有些笨重但非常直接.


Rou*_*oun 11

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

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


Phi*_* L. 10

我更喜欢使用" - "作为你应该从stdin读取的指标,它更明确:

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

  • @TimofeyBondarev 这可能是真的……但最常见的是,输入只在脚本中使用一次。这是一个有用的构造。 (3认同)
  • 你的解决方案将关闭`sys.stdin`,所以`with`语句之后的`input`函数调用将引发`ValueError`. (2认同)

Cur*_*son 8

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

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())
Run Code Online (Sandbox Code Playgroud)

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

p.add_argument('input', type=FileType(encoding='UTF-8'))
Run Code Online (Sandbox Code Playgroud)

请注意,后一种情况将不支持二进制模式 ( 'b') I/O。如果您需要这个,您可以使用上面的默认参数技术,但提取二进制 I/O 对象,例如default=sys.stdout.bufferstdout。但是,如果用户-无论如何指定,这仍然会中断。(对于-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
Run Code Online (Sandbox Code Playgroud)

(向medhat致敬,提及add_argument()type参数。)


Med*_*hat 7

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

通常,当您编写python脚本时,可以使用该argparse软件包。在这种情况下,您可以使用:

parser = argparse.ArgumentParser()
parser.add_argument('infile', nargs='?', type=argparse.FileType('r'), default=sys.stdin)
Run Code Online (Sandbox Code Playgroud)

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

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

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

欲了解更多信息,请访问:https : //docs.python.org/2/library/argparse.html#nargs