Как читать из стандартного ввода или из файла, если в Python нет данных? - PullRequest
17 голосов
/ 15 февраля 2010

У меня есть скрипт CLI, и я хочу, чтобы он считывал данные из файла. Это должно быть в состоянии прочитать это двумя способами:

  • cat data.txt | ./my_script.py
  • ./my_script.py data.txt

- немного похоже, например, grep.

Что я знаю:

  • sys.argv и optparse позвольте мне легко прочитать любые аргументы и варианты.
  • sys.stdin позвольте мне прочитать данные по
  • fileinput сделать весь процесс автоматическим

К сожалению:

  • с использованием fileinput использует стандартный ввод и любые аргументы в качестве входных данных. Поэтому я не могу использовать параметры, которые не являются именами файлов, так как он пытается открыть их.
  • sys.stdin.readlines() работает нормально, но если я не передаю данные по конвейеру, он зависает, пока я не введу Ctrl + D
  • Я не знаю, как реализовать "если ничего в stdin, читать из файла в args", потому что stdin всегда True в логическом контексте.

Я бы хотел, чтобы это был портативный способ, если это возможно.

Ответы [ 6 ]

20 голосов
/ 15 февраля 2010

Argparse позволяет сделать это довольно простым способом, и вы действительно должны использовать его вместо optparse, если у вас нет проблем совместимости.

Код будет выглядеть примерно так:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--input', type = argparse.FileType('r'), default = '-')

Теперь у вас есть анализатор, который будет анализировать аргументы командной строки, использовать файл, если он его видит, или использовать стандартный ввод, если его нет.

10 голосов
/ 15 февраля 2010

Обрабатывайте аргументы не из имен файлов так, как вам хочется, поэтому вы получите массив аргументов без опций, а затем передадите этот массив в качестве параметра в fileinput.input ():

import fileinput
for line in fileinput.input(remaining_args):
    process(line)
9 голосов
/ 15 февраля 2010

Для unix / linux вы можете определить, передаются ли данные по каналу, взглянув на os.isatty(0)

$ date | python -c "import os;print os.isatty(0)"
False
$ python -c "import os;print os.isatty(0)"
True

Я не уверен, что есть эквивалент для Windows.

редактировать Хорошо, я попробовал это с python2.6 на Windows XP

C:\Python26>echo "hello" | python.exe -c "import os;print os.isatty(0)"  
False

C:\Python26> python.exe -c "import os;print os.isatty(0)"  
True

Так что, может быть, это не все безнадежно для Windows

4 голосов
/ 01 февраля 2013

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

Последняя комбинация, которую я собрал:

parser = argparse.ArgumentParser()
parser.add_argument("infiles", nargs="*")
args = parser.parse_args()

for line in fileinput.input(args.infiles):
    process(line)

Это кажется единственным способом получить все желаемое поведение в одном элегантном пакете, не требуя именованных аргументов. Также как команды unix используются как таковые:

cat file1 file2
wc -l < file1

Не

cat --file file1 --file file2

Буду признателен за обратную связь / подтверждение от старых идиоматических Pythonistas, чтобы убедиться, что у меня есть лучший ответ. Никогда не видел это полное решение, упомянутое где-либо еще, только фрагменты.

3 голосов
/ 15 февраля 2010

Не существует надежного способа определить, подключен ли sys.stdin к чему-либо, и это нецелесообразно (например, пользователь хочет вставить данные). Определите наличие имени файла в качестве аргумента и используйте стандартный ввод, если ничего не найдено.

2 голосов
/ 11 июля 2012

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

sys.stdin.isatty()

Возвращает false, если входные данные получены из конвейера, или true в противном случае.

...