Связь несколько раз с подпроцессом (несколько вызовов на стандартный вывод) - PullRequest
0 голосов
/ 02 мая 2011

В [этой теме] [1] есть похожий вопрос к моему.

Я хочу отправить команду своему подпроцессу, интерпретировать ответ, а затем отправить другую команду. Казалось бы, стыдно начинать новый подпроцесс, чтобы выполнить это, особенно если subprocess2 должен выполнять многие из тех же задач, что и subprocess1 (например, ssh, open mysql).

Я попробовал следующее:

subprocess1.stdin.write([my commands])
subprocess1.stdin.flush()
subprocess1.stout.read()

Но без определенного параметра для байтов до read() программа застревает при выполнении этой инструкции, и я не могу предоставить аргумент для read(), потому что я не могу угадать, сколько байтов доступно в потоке.

Я использую WinXP, Py2.7.1

EDIT

Благодарю @regularfry за то, что он дал мне лучшее решение для моих реальных намерений (прочитайте комментарии в его ответе, так как они касаются достижения моей цели через туннель SSH). (За его / ее ответ проголосовали.) Однако в интересах любого зрителя, который в дальнейшем приходит за ответом на заглавный вопрос, я принял ответ @Mike Penningtion.

Ответы [ 3 ]

2 голосов
/ 02 мая 2011

Ваш выбор:

  1. Используйте линейно-ориентированный протокол (и используйте readline () вместо read ()) и убедитесь, что каждая возможная отправленная строка является допустимым сообщением;
  2. Используйте read (1) и парсер, чтобы сообщить вам, когда вы прочитали полное сообщение; или
  3. Извлечение объектов сообщения в поток из подпроцесса, затем удаление их из родительского объекта. Это решает проблему длины сообщения для вас.
1 голос
/ 04 мая 2011

Этот подход будет работать (я это сделал), но займет некоторое время и использует специфичные для Unix вызовы. Вам придется отказаться от модуля подпроцесса и свернуть свой собственный эквивалент на основе fork / exec и os.pipe ().

Используйте функцию fcntl.fcntl, чтобы перевести файловые дескрипторы stdin / stdout (чтение и запись) для вашего дочернего процесса в неблокирующий режим (константа опции O_NONBLOCK) после создания их с помощью os.pipe ().

Используйте функцию select.select, чтобы опросить или дождаться доступности ваших файловых дескрипторов. Чтобы избежать взаимных блокировок, вам нужно будет использовать select (), чтобы гарантировать, что записи не будут блокироваться, как и чтения. Тем не менее, вы должны учитывать исключения OSError при чтении и записи и повторять попытки при появлении ошибок EAGAIN. (Даже при использовании select перед чтением / записью EAGAIN может возникать в неблокирующем режиме; это распространенная ошибка ядра, которую трудно исправить.)

Если вы готовы реализовать на Twisted Framework, они, якобы, решили эту проблему для вас; все, что вам нужно сделать, это написать подкласс Process. Но я сам этого еще не пробовал.

1 голос
/ 03 мая 2011

@ JellicleCat, я отслеживаю комментарии. Я полагаю, что wexpect является частью sage ... AFAIK, он не упакован отдельно, но вы можете скачать wexpect здесь .

Честно говоря, если вы собираетесь вести программные сессии ssh, используйте paramiko . Он поддерживается как самостоятельная установка, имеет хорошую упаковку и должен устанавливаться непосредственно на Windows.

EDIT

Пример сценария paramiko для перехода в каталог, выполнения ls и выхода ... захвата всех результатов ...

import sys
sys.stderr = open('/dev/null')       # Silence silly warnings from paramiko
import paramiko as pm
sys.stderr = sys.__stderr__
import os

class AllowAllKeys(pm.MissingHostKeyPolicy):
    def missing_host_key(self, client, hostname, key):
        return

HOST = '127.0.0.1'
USER = ''
PASSWORD = ''

client = pm.SSHClient()
client.load_system_host_keys()
client.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
client.set_missing_host_key_policy(AllowAllKeys())
client.connect(HOST, username=USER, password=PASSWORD)

channel = client.invoke_shell()
stdin = channel.makefile('wb')
stdout = channel.makefile('rb')

stdin.write('''
cd tmp
ls
exit
''')
print stdout.read()

stdout.close()
stdin.close()
client.close()
...