Подпроцесс readline зависает в ожидании EOF - PullRequest
2 голосов
/ 26 октября 2011

У меня есть простая программа на С ++, которую я пытаюсь выполнить с помощью скрипта Python. (Я очень новичок в написании скриптов) и у меня возникают проблемы с чтением вывода через канал. Из того, что я видел, похоже, что readline () не будет работать без EOF, но я хочу иметь возможность читать в середине программы и заставить скрипт реагировать на то, что выводится. Вместо того, чтобы читать вывод, он просто зависает скрипт python:

#!/usr/bin/env python
import subprocess
def callRandomNumber():
    print "Running the random guesser"
    rng=subprocess.Popen("./randomNumber", stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True)
    i=50
    rng.stdin.write("%d\n" % i)
    output=rng.stdout.readline()
    output=rng.stdout.readline()
callRandomNumber()

и файл c ++, который генерирует случайное число от одного до 100, затем проверяет, угадывают ли пользователи, пока они не угадывают правильно

#include<iostream>
#include<cstdlib>
using namespace std;

int main(){
  cout<<"This program generates a random number from 1 to 100 and asks the user to enter guesses until they succuessfully guess the number.  It then tells the user how many guesses it took them"<<endl;
  srand(time(NULL));
  int num=rand()%100;
  int guessCount=0;
  int guess=-1;
  cout<<"Please enter a number:  ";
  cin>>guess;
  while(guess!=num){
    if(guess>num){cout<<"That guess is too high.  Please guess again:  ";}
    else{cout<<"That guess is too low.  Please guess again:  ";}
    cin>>guess;
    guessCount++;
  }
  cout<<"Congratulations!  You solved it in "<<guessCount<<" guesses!"<<endl;
  return 0;
}

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

Ответы [ 3 ]

3 голосов
/ 22 мая 2014

Как указал @ Рон Рейтер , вы не можете использовать readline(), потому что cout не печатает новые строки неявно - вам нужно либо std::endl, либо "\n" здесь.

Для интерактивного использования, когда вы не можете изменить дочернюю программу, pexpect модуль предоставляет несколько удобных методов (и в целом он решает бесплатно: ввод / вывод напрямую из/ на терминал (вне stdin / stdout) и проблемы с буферизацией блока ):

#!/usr/bin/env python
import sys

if sys.version_info[:1] < (3,):
    from pexpect import spawn, EOF # $ pip install pexpect
else:
    from pexpect import spawnu as spawn, EOF # Python 3

child = spawn("./randomNumber") # run command
child.delaybeforesend = 0 
child.logfile_read = sys.stdout # print child output to stdout for debugging
child.expect("enter a number: ") # read the first prompt
lo, hi = 0, 100
while lo <= hi:
    mid = (lo + hi) // 2
    child.sendline(str(mid)) # send number
    index = child.expect([": ", EOF]) # read prompt
    if index == 0: # got prompt
        prompt = child.before
        if "too high" in prompt:
            hi = mid - 1 # guess > num
        elif "too low" in prompt:
            lo = mid + 1 # guess < num
    elif index == 1: # EOF
        assert "Congratulations" in child.before
        child.close()
        break
else:
    print('not found')
    child.terminate()
sys.exit(-child.signalstatus if child.signalstatus else child.exitstatus)

Работает, но это двоичный поиск, поэтому (традиционно) могут быть ошибки .

Вот аналогичный код, который для сравнения использует модуль subprocess:

#!/usr/bin/env python
from __future__ import print_function
import sys
from subprocess import Popen, PIPE

p = Popen("./randomNumber", stdin=PIPE, stdout=PIPE,
          bufsize=1, # line-buffering
          universal_newlines=True) # enable text mode
p.stdout.readline() # discard welcome message: "This program gener...

readchar = lambda: p.stdout.read(1)
def read_until(char):
    buf = []
    for c in iter(readchar, char):
        if not c: # EOF
            break
        buf.append(c)
    else: # no EOF
        buf.append(char)
    return ''.join(buf).strip()

prompt = read_until(':') # read 1st prompt
lo, hi = 0, 100
while lo <= hi:
    mid = (lo + hi) // 2
    print(prompt, mid)
    print(mid, file=p.stdin) # send number
    prompt = read_until(':') # read prompt
    if "Congratulations" in prompt:
        print(prompt)
        print(mid)
        break # found
    elif "too high" in prompt:
        hi = mid - 1 # guess > num
    elif "too low" in prompt:
        lo = mid + 1 # guess < num
else:
    print('not found')
    p.kill()
for pipe in [p.stdin, p.stdout]:
    try:
        pipe.close()
    except OSError:
        pass
sys.exit(p.wait())
1 голос
/ 26 октября 2011

Я почти уверен, что добавление новых строк в вашу C ++ программу приведет к возврату readlines.

0 голосов
/ 20 мая 2014

Возможно, вам придется явно закрыть stdin, чтобы дочерний процесс перестал зависать, что, я думаю, и происходит с вашим кодом - это можно проверить, запустив top на терминале и проверив, если randomnumber ' Статус s остается спящим, и если он использует 0% CPU после ожидаемого времени, которое потребуется для выполнения.

Короче говоря, если вы добавите rng.stdin.close() сразу после вызова rng=subprocess(...), он может возобновиться без проблем. Другой вариант - сделать output=rng.communicate(stdin="%d\n" % i) и посмотреть на output[0] и output[1], которые равны stdout и stderr соответственно. Вы можете найти информацию о communicate здесь .

...