Чтение из файла или STDIN

Я написал утилиту командной строки, которая использует getopt для анализа аргументов, заданных в командной строке. Я также хотел бы, чтобы имя файла было необязательным аргументом, например, в других утилитах, таких как grep, cut и т.д. Поэтому я хотел бы иметь следующее использование

tool -d character -f integer [filename]

Как я могу реализовать следующее?

  • Если задано имя файла, прочитайте его из файла.
  • если имя файла не указано, прочитайте из STDIN.

Ответ 1

В простейших терминах:

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

В этот момент вы должны использовать inf для чтения из файла. В зависимости от того, было ли задано имя файла, это будет считаться из данного файла или из stdin.

Когда вам нужно закрыть файл, вы можете сделать это:

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

Однако в большинстве случаев было бы безвредно закрыть sys.stdin, если вы закончите с ним.

Ответ 2

Модуль fileinput может делать то, что вы хотите - если аргументы без параметра находятся в args, то:

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

Если args пусто, тогда fileinput.input() будет читать из stdin; в противном случае он читает из каждого файла по очереди, аналогично Perl while(<>).

Ответ 3

Мне нравится общая идиома использования диспетчера контекста, но (слишком) тривиальное решение заканчивается закрытием 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)

Я полагаю, вы могли бы достичь чего-то подобного с os.dup(), но код, который я приготовил для этого, оказался более сложным и более волшебным, тогда как выше является несколько неуклюжим, но очень простым.

Ответ 4

Чтобы использовать оператор python with, можно использовать следующий код:

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

Ответ 5

Я предпочитаю использовать "-" в качестве индикатора, который следует читать из stdin, более явным:

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

Ответ 6

Что-то вроде:

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