2010-02-15 1 views
17

У меня есть сценарий CLI и вы хотите, чтобы он считывал данные из файла. Он должен быть в состоянии прочитать его двумя способами:Как читать из stdin или из файла, если в Python нет данных?

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

-a немного как grep, например.

То, что я знаю:

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

К сожалению:

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

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

+0

Спасибо, я многому научился сегодня. –

ответ

10

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

import fileinput 
for line in fileinput.input(remaining_args): 
    process(line) 
+0

Мне это нравится, кажется очень эффективным. Что-то плохое мне не хватает? –

+0

Я считаю, что это обеспечит поведение, подобное другим командам Unix. В другом комментарии вы сказали, что вас немного беспокоит эффект зависания до ввода, когда аргументы не указаны; если вы не предпримете шаги, чтобы заметить, когда аргументы не пройдены, это все равно произойдет. Нет причин, по которым вы не должны улавливать этот случай, поскольку передаете «-», поскольку параметр все равно будет читать из stdin. –

+0

Я воспользуюсь сочетанием совета Игносио и решения Эндрю. –

3

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

+0

Спасибо вам обоим. Поскольку у меня нет необходимых знаний, чтобы выбирать между вашим ответом и гниблером, каковы недостатки каждого из них? Они кажутся действительными. –

+0

О, просто попробовал этот подход, но их проблема: если нет файлов и нет stdin, я заканчиваю чтение stdin и зависает. Как я могу написать сообщение об ошибке пользователю, чтобы сообщить ему о предоставлении данных? –

+0

Заметьте, что зависание до EOF неизбежно здесь, если кто-то запускает его без имени файла - это стандартное поведение в мире Unix. см. также grep, cat и т. д. Если это неприемлемо, единственный переносимый способ избежать этого - использовать другое типичное соглашение, в котором предоставление имени файла «-» означает «чтение из stdin» (или запись в stdout) , –

8

Для 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 на окнах 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 

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

+1

Спасибо вам обоим. Поскольку у меня нет необходимых знаний для выбора между вашим ответом и Игнооцио, каковы недостатки каждого из них? Они кажутся действительными. –

+0

@ e-satis: Что произойдет, если имя файла не будет передано в качестве аргумента? Когда вы сможете ответить на этот вопрос, вы узнаете, что вам нужно сделать. –

+0

На данный момент это работает, поэтому я принимаю его. Еще раз спасибо. Мне все еще интересно узнать о проблемах с тем, что я делаю. Что такое tty, и почему это работает? Когда это будет? –

20

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

код будет идти-то вроде этого:

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

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

+0

Спасибо вам. Я многому учусь сегодня! Во всяком случае, разве вы не думаете, что есть способ сделать это со стандартным lib? Если нет, я в порядке с argparse. Но optparse действительно существует ... –

+0

argparse довольно крошечный, является чистым кодом на Python и также приятнее использовать, чем optparse. Хотя я обычно не хотел бы добавлять новую зависимость к проекту, просто чтобы прочитать в параметрах командной строки, три фактора выше сделали argparse более чем полезным в моем опыте. – mavnn

+0

Это можно сделать в том же объеме кода в 'optparse'. –

2

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

sys.stdin.isatty() 

Он возвращает значение false, если вход от трубопровода или истина в противном случае.

+0

Поведение вашей программы не должно зависеть от того, связано ли 'stdin' с tty. Если имена файлов не указаны, просто прочитайте их из 'stdin', будь то из tty или pipe. – musiphil

+0

Это действительно помогло мне. Мне нужно было знать, были ли мои данные переданы по каналам или tty для конкретного проекта, и это было единственным хорошим решением, которое я мог бы использовать. – Blairg23

4

Я ноб, поэтому это может быть не очень хороший ответ, но я пытаюсь сделать то же самое (разрешить один или несколько файлов в командной строке, по умолчанию - 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 

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

+1

Еще одна вещь - если вы не хотите зависеть от следующего парня, который уже знает, что fileinput.input() по умолчанию задает stdin, когда он получает пустой список, вы можете добавить ', default = "-"' в позвоните в add_argument(). Это ничего не меняет, но делает логику совершенно явной. – odigity

Смежные вопросы