2009-11-16 3 views
49

Я написал утилиту командной строки, которая использует getopt для анализа аргументов, указанных в командной строке. Я также хотел бы иметь имя файла будет необязательным аргументом, например, как в других утилит, как Grep, вырезать и т.д. Таким образом, я хотел бы его иметь следующие UsageЧтение из файла или STDIN

tool -d character -f integer [filename] 

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

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

см. Также http://unix.stackexchange.com/questions/47098/how-do-i-make-python-programs-behave-like-proper-unix-tools/47543#47543 – magnetar

ответ

47

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

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 если вы сделали с ним.

+0

Будет ли raw_input() и input() читать из inf? – thefourtheye

+0

@ thefourtheye: Да, обе эти функции будут прочитаны либо из файла, либо из 'sys.stdin'. –

+2

Я нашел другой способ решить эту проблему, я написал об этом здесь http://dfourtheye.blogspot.in/2013/05/python-equivalent-of-cs-freopen.html и добавил ответ на этот вопрос. – thefourtheye

61

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

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

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

+0

Это было так же хорошо ответа, но не настолько обобщаемо. Я буду помнить, что в следующий раз при необходимости буду использовать fileinput. –

+0

Он работает без 'args'. – Gabriel

+0

Правильно, но если вы используете 'getargs' (как OP), то вы, вероятно, просто хотите передать оставшиеся args, а не' sys.argv [1:] '(который по умолчанию). – SimonJ

0

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

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

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

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

Ваше решение закрывает 'sys.stdin', поэтому вызовы функций' input' после '' '' '' '' '' '' '' вызывает 'ValueError'. –

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 
+2

Ваше решение закрывает 'sys.stdin', поэтому вызовы функций' input' после '' '' '' '' '' '' '' вызывает 'ValueError'. –

+1

@TimofeyBondarev Это может быть правдой .. но чаще всего вход используется только один раз в скрипте. Это полезная конструкция. – javadba

11

Мне нравится общая идиома использования контекстного управления er, но (тривиальное) решение заканчивается закрытием sys.stdin, когда вы находитесь вне инструкции with, чего я хочу избежать.

Заимствуя this answer, вот обходной путь:

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) 

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

+0

Спасибо большое! Именно этого я и искал. Очень четкое и прямолинейное решение. – edisonex