1

我试图从子进程的输出中捕获一个字符串,当子进程要求用户输入时,将用户输入包含在字符串中,但我无法让 stdout 工作。

我使用while循环从stdout获得了字符串输出,但我不知道如何在读取字符串后终止它。

我尝试使用subprocess.check_output,但是我看不到用户输入的提示。

import subprocess
import sys
child = subprocess.Popen(["java","findTheAverage"], stdout = subprocess.PIPE, stdin = subprocess.PIPE )
string = u""

while True:
    line = str(child.stdout.read(1))
    if line != '':
        string += line[2]
        print(string)
    else:
        break

print(string)
for line in sys.stdin:
    print(line)
    child.stdin.write(bytes(line, 'utf-8'))

编辑:

在 Alfe 帖子的帮助和代码的帮助下,我现在从子流程程序输出中创建了一个字符串,并且用户输入了该程序,但它混乱了。

该字符串似乎首先得到输出的第一个字母,然后是用户输入,然后是输出的其余部分。

字符串混淆示例:

U2
3ser! please enter a double:U
4ser! please enter another double: U
5ser! please enter one final double: Your numbers were:
a = 2.0
b = 3.0
c = 4.0
average = 3.0

意为:

User! please enter a double:2
User! please enter another double: 3
User! please enter one final double: 4
Your numbers were:
a = 2.0
b = 3.0
c = 4.0
average = 3.0

使用代码:

import subprocess
import sys
import signal
import select


def signal_handler(signum, frame):
    raise Exception("Timed out!")


child = subprocess.Popen(["java","findTheAverage"], universal_newlines = True, stdout = subprocess.PIPE, stdin = subprocess.PIPE )
string = u""
stringbuf = ""

while True:
  print(child.poll())
  if child.poll() != None and not stringbuf:
    break
  signal.signal(signal.SIGALRM, signal_handler)
  signal.alarm(1)
  try:
    r, w, e = select.select([ child.stdout, sys.stdin ], [], [])
    if child.stdout in r:
        stringbuf = child.stdout.read(1)
        string += stringbuf
        print(stringbuf)
  except:
    print(string)
    print(stringbuf)
  if sys.stdin in r:
    typed = sys.stdin.read(1)
    child.stdin.write(typed)
    string += typed

最终编辑:

好吧,我玩弄了它并让它使用以下代码:

import subprocess
import sys
import select
import fcntl
import os

# the string that we will return filled with tasty program output and user input #
string = ""

# the subprocess running the program #
child = subprocess.Popen(["java","findTheAverage"],bufsize = 0, universal_newlines = True, stdout = subprocess.PIPE, stdin = subprocess.PIPE )

# stuff to stop IO blocks in child.stdout and sys.stdin ## (I stole if from  http://stackoverflow.com/a/8980466/2674170)
fcntl.fcntl(child.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)

# this here in the unlikely event that the program has #
# finished by the time the main loop is first running  #
# because if that happened the loop would end without  #
# having added the programs output to the string!      #
progout = ""
typedbuf = "#"

### here we have the main loop, this friendly fellah is 
### going to read from the program and user, and tell
### each other what needs to be known
while True:

## stop when the program finishes and there is no more output
  if child.poll() != None and not progout:
    break

# read from 
  typed = ""

  while typedbuf:
    try:
      typedbuf = sys.stdin.read(1)
    except:
      break
    typed += typedbuf
    stringbuf  = "#"
  string += typed
  child.stdin.write(typed)

  progout = ""
  progoutbuf = "#"
  while progoutbuf:
    try:
      progoutbuf = child.stdout.read(1)
    except:
      typedbuf = "#"
      break 
    progout += progoutbuf
  if progout:
    print(progout)
  string += progout

# the final output string #
print( string)
4

1 回答 1

1

您需要select同时从多个源中读取(在您的情况下为标准输入和子进程的输出)。

import select

string = ''
while True:
  r, w, e = select.select([ child.stdout, sys.stdin ], [], [])
  if child.stdout in r:
    string += child.stdout.read()
  if sys.stdin in r:
    typed = sys.stdin.read()
    child.stdin.write(typed)
    string += typed

您仍然需要找到适当的中断条件才能离开该循环。但你可能已经明白了。

我想在这一点上发出警告:写入管道的进程通常会缓冲直到可能的最新时刻;您可能不会想到这一点,因为从命令行(在终端中)测试相同的程序时,通常只有行被缓冲。这是出于性能考虑。写入终端时,用户通常希望尽快看到输出。当写入管道时,通常读取进程很乐意获得更大的块,以便在它们到达之前睡得更久。

于 2013-10-02T14:55:17.237 回答