子进程readline挂起等待EOF

时间:2011-10-25 23:50:59

标签: python subprocess stdout pipe readline

我有一个简单的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 ++文件,生成1到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 个答案:

答案 0 :(得分:3)

作为@Ron Reiter pointed out,您无法使用readline(),因为cout无法隐式打印换行符 - 您需要std::endl"\n"

对于交互式使用,当您无法更改子程序时,pexpect module提供了一些便捷方法(通常为it solves for free: input/output directly from/to terminal (outside of stdin/stdout) and block-buffering issues):

#!/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)

它有效,但它是二进制搜索(traditionally) there could be bugs

以下是使用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 :(得分:1)

我很确定在C ++程序中添加换行符会导致返回读取行。

答案 2 :(得分:0)

您可能必须明确关闭stdin,因此子进程将停止挂起,我认为您的代码正在发生 - 这可以通过在终端上运行top并检查{{{ 1}}状态保持休眠状态,如果在执行所需的预期时间后使用0%CPU。

简而言之,如果您在randomnumber电话之后立即添加rng.stdin.close(),则可能会毫无问题地恢复。另一种选择是rng=subprocess(...)并分别查看output=rng.communicate(stdin="%d\n" % i)output[0]的{​​{1}}和output[1]。您可以在stdout here找到相关信息。