Последовательный способ перенаправления как stdin, так и stdout в файлы на python с использованием optparse

#python #stdout #stdin #optparse

#python #стандартный вывод #stdin #optparse

Вопрос:

У меня есть дюжина программ, которые могут принимать ввод через stdin или option, и я хотел бы реализовать те же функции аналогичным образом для вывода.

Код optparse выглядит следующим образом:

 parser.add_option('-f', '--file',
       default='-',
       help='Specifies the input file.  The default is stdin.')
parser.add_option('-o', '--output',
       default='-',
       help='Specifies the output file.  The default is stdout.')
 

Остальная часть применимого кода выглядит следующим образом:

 if opts.filename == '-':
    infile = sys.stdin
else:
    infile = open(opts.filename, "r")

if opts.output == '-':
    outfile = sys.stdout
else:
    outfile = open(opts.output, "w")
 

Этот код работает нормально, и мне нравится его простота, но я не смог найти ссылку на кого-либо, использующего значение по умолчанию ‘-‘ для вывода, чтобы указать stdout . Является ли это хорошим согласованным решением или я упускаю из виду что-то лучшее или более ожидаемое?

Комментарии:

1. Небольшое уточнение — в прошлом я видел публикации, в которых для индикатора в optparse / argparse рекомендуется ставить тире, которое программа должна считывать из stdin. Но я не видел этого для stdout. Я понятия не имею, что это за фон для этого — звучит знакомо? Есть идеи? В противном случае предложение Адама Вагнера выглядит немного более простым и понятным.

Ответ №1:

Для входных файлов вы можете использовать fileinput module . Он следует общему соглашению для входных файлов: если не указаны файлы или имя файла ‘-‘, он считывает stdin, в противном случае он считывает из файлов, заданных в командной строке.

Нет необходимости в -f --file параметрах и . Если вашей программе всегда требуется входной файл, то это не вариант.

-o и --output используется для указания имени выходного файла в различных программах.

optparse

 #!/usr/bin/env python
import fileinput
import sys
from optparse import OptionParser

parser = OptionParser()
parser.add_option('-o', '--output',
    help='Specifies the output file.  The default is stdout.')
options, files = parser.parse_args()
if options.output and options.output != '-':
   sys.stdout = open(options.output, 'w')

for line in fileinput.input(files):
    process(line)
 

argparse

argparse модуль позволяет явно указывать файлы в качестве аргументов:

 #!/usr/bin/env python
import fileinput
import sys
from argparse import ArgumentParser

parser = ArgumentParser()
parser.add_argument('files', nargs='*', help='specify input files')
group = parser.add_mutually_exclusive_group()
group.add_argument('-o', '--output', 
    help='specify the output file.  The default is stdout')
group.add_argument('-i', '--inplace', action='store_true',
    help='modify files inplace')
args = parser.parse_args()

if args.output and args.output != '-':
   sys.stdout = open(args.output, 'w')

for line in fileinput.input(args.files, inplace=args.inplace):
    process(line)
 

Примечание: я добавил --inplace опцию во втором примере:

 $ python util-argparse.py --help
usage: util-argparse.py [-h] [-o OUTPUT | -i] [files [files ...]]

positional arguments:
  files                 specify input files

optional arguments:
  -h, --help            show this help message and exit
  -o OUTPUT, --output OUTPUT
                        specify the output file. The default is stdout
  -i, --inplace         modify files inplace
 

Комментарии:

1. Модули fileinput и csv не выглядят совместимыми для меня, поэтому я не использую fileinput для этих программ. Я ошибаюсь в этом предположении?

2. @KenFar: что вы подразумеваете под «совместимым»? csv.reader(fileinput.input()) работает, как и ожидалось.

3. Упс, моя ошибка. Похоже, это лучшее решение, поскольку оно также может принимать несколько входных файлов. Спасибо.

4. @J.F. Себастьян, привет, я заметил, что в примере argpase, когда выводом является файл, код явно не вызывает close() , это нормально?

5. да, вы хотите оставить stdout открытым до завершения вашего скрипта.

Ответ №2:

Если вы можете использовать argparse (например, Python 2.7 ), он имеет встроенную поддержку того, что вы хотите: прямо из argparse документа

FileType Фабрика создает объекты, которые могут быть переданы в аргумент типа ArgumentParser.add_argument() . Аргументы, имеющие FileType объекты в качестве своего типа, откроют аргументы командной строки […] FileType объекты понимают псевдоаргумент ‘-‘ и автоматически преобразуют его в sys.stdin для читаемых FileType объектов и sys.stdout FileType для объектов, доступных для записи.

Поэтому мой совет — просто использовать

 import sys
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('file', type=argparse.FileType('r'),
    help="Specifies the input file")
parser.add_argument('output', type=argparse.FileType('w'),
    help="Specifies the output file")
args = parser.parse_args(sys.argv[1:])

# Here you can use your files
text = args.file.read()
args.output.write(text)
# … and so on
 

Затем вы можете сделать

 > python spam.py file output 
 

Для чтения file и вывода в output , или

 > echo "Ni!" | python spam.py - output  
 

для чтения "Ni!" и вывода в output , или

 > python spam.py file -
 

И это хорошо, поскольку использование - для соответствующего потока — это соглашение, которое используют многие программы. Если вы хотите указать на это, добавьте его в help строки.

   parser.add_argument('file', type=argparse.FileType('r'),
    help="Specifies the input file, '-' for standard input")
 

Для справки, сообщение об использовании будет

 > python spam.py -h
usage: [-h] file output

positional arguments:
  file        Specifies the input file, '-' for standard input
  output      Specifies the output file, '-' for standard output

optional arguments:
  -h, --help  show this help message and exit