Read from File, or STDIN

2019-01-21 02:52发布

I've written a command line utility that uses getopt for parsing arguments given on the command line. 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

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.

7条回答
一夜七次
2楼-- · 2019-01-21 03:39

To make use of python's with statement, one can use the following code:

import sys
with open(sys.argv[1], 'r') if len(sys.argv) > 1 else sys.stdin as f:
    # read data using f
    # ......
查看更多
Anthone
3楼-- · 2019-01-21 03:43

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. 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.

查看更多
▲ chillily
4楼-- · 2019-01-21 03:47

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
查看更多
做自己的国王
5楼-- · 2019-01-21 03:50

The fileinput module may do what you want - assuming the non-option arguments are in args then:

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

If args is empty then fileinput.input() will read from stdin; otherwise it reads from each file in turn, in a similar manner to Perl's while(<>).

查看更多
ゆ 、 Hurt°
6楼-- · 2019-01-21 03:50

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()
查看更多
beautiful°
7楼-- · 2019-01-21 03:56

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.

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

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.

查看更多
登录 后发表回答