использовать генератор как вход подпроцесса;получил исключение "операция ввода-вывода для закрытого файла" - PullRequest
2 голосов
/ 27 марта 2012

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

cmd = ['intersectBed', '-a', 'stdin', '-b', bedfile]
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
for entry in my_entry_generator: # <- this is my generator
    output = p.communicate(input='\t'.join(entry) + '\n')[0]
    print output

Я прочитал еще один похожий вопрос, в котором используется p.stdin.write. но все равно была та же проблема.

Что я не так сделал?

[править] Я заменил последние два утверждения следующим (спасибо SpliFF):

    output = p.communicate(input='\t'.join(entry) + '\n')
    if output[1]: print "error:", output[1]
    else: print output[0]

чтобы узнать, была ли какая-либо ошибка внешней программой. Но нет. Все еще есть то же исключение на линии p.communicate.

Ответы [ 2 ]

5 голосов
/ 27 марта 2012

Метод communicate объектов subprocess.Popen может быть вызван только один раз.Он отправляет введенные вами данные процессу , а читает все выходные данные stdout и stderr.Под словом «все» я подразумеваю, что он ожидает завершения процесса, чтобы он знал, что у него есть все выходные данные.После возврата communicate процесс больше не существует.

Если вы хотите использовать communicate, вам нужно либо перезапустить процесс в цикле, либо присвоить ему одну строку, которая all вход от генератора.Если вы хотите осуществлять потоковую связь, посылая данные побитно, вам не нужно использовать communicate.Вместо этого вам нужно будет написать в p.stdin при чтении из p.stdout и p.stderr.Делать это сложно, потому что вы не можете сказать, какой вывод вызван каким вводом, и потому что вы можете легко столкнуться с тупиками.В этом вам могут помочь сторонние библиотеки, например Twisted.

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

Конечно, если вам удастся просто запустить процесс внутри цикла, это будет намного проще:

cmd = ['intersectBed', '-a', 'stdin', '-b', bedfile]
for entry in my_entry_generator:
    p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    output = p.communicate(input='\t'.join(entry) + '\n')[0]
    print output
0 голосов
/ 27 марта 2012

Возможно, ваше приложение intersectBed завершает работу с ошибкой, но, поскольку вы не печатаете данные stderr, вы не можете их увидеть. Попробуйте:

result = p.communicate(input='\t'.join(entry) + '\n')
if result[1]:
  print "error:", result[1]
else:
  print result[0]
...