Подпроцесс readline зависает в ожидании EOF
У меня есть простая программа на C++, которую я пытаюсь выполнить с помощью скрипта 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 ответа
Как заметил @Ron Reiter, вы не можете использовать 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())
Я почти уверен, что добавление новых строк в вашу C++ программу приведет к возврату readlines.
Возможно, вам придется явно закрытьstdin
Таким образом, дочерний процесс перестанет зависать, что, я думаю, и происходит с вашим кодом - это можно проверить, запустив top на терминале и проверив, randomnumber
Статус остается спящим, и если он использует 0% ЦП после ожидаемого времени, которое потребуется для выполнения.
Короче если добавить rng.stdin.close()
сразу после rng=subprocess(...)
позвоните, это может возобновиться без проблем. Другим вариантом будет сделать output=rng.communicate(stdin="%d\n" % i)
и посмотри на output[0]
а такжеoutput[1]
кто stdout
а также stderr
соответственно. Вы можете найти информацию наcommunicate
здесь