使用Python的交互式输入/输出

sauutmhj  于 2024-01-05  发布在  Python
关注(0)|答案(4)|浏览(170)

我有一个与用户交互的程序(像shell一样),我想使用Python子进程模块交互地运行它。这意味着,我希望有可能写入标准输入并立即从标准输出获得输出。我尝试了这里提供的许多解决方案,但似乎没有一个能满足我的需求。
我写的代码基于 Running an interactive command from within Python

  1. import Queue
  2. import threading
  3. import subprocess
  4. def enqueue_output(out, queue):
  5. for line in iter(out.readline, b''):
  6. queue.put(line)
  7. out.close()
  8. def getOutput(outQueue):
  9. outStr = ''
  10. try:
  11. while True: # Adds output from the queue until it is empty
  12. outStr += outQueue.get_nowait()
  13. except Queue.Empty:
  14. return outStr
  15. p = subprocess.Popen("./a.out", stdin=subprocess.PIPE, stout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize = 1)
  16. #p = subprocess.Popen("./a.out", stdin=subprocess.PIPE, stout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False, universal_newlines=True)
  17. outQueue = Queue()
  18. errQueue = Queue()
  19. outThread = Thread(target=enqueue_output, args=(p.stdout, outQueue))
  20. errThread = Thread(target=enqueue_output, args=(p.stderr, errQueue))
  21. outThread.daemon = True
  22. errThread.daemon = True
  23. outThread.start()
  24. errThread.start()
  25. p.stdin.write("1\n")
  26. p.stdin.flush()
  27. errors = getOutput(errQueue)
  28. output = getOutput(outQueue)
  29. p.stdin.write("5\n")
  30. p.stdin.flush()
  31. erros = getOutput(errQueue)
  32. output = getOutput(outQueue)

字符串
问题是队列仍然是空的,就好像没有输出一样。只有当我把程序需要执行和终止的所有输入都写入标准输入时,我才能得到输出(这不是我想要的)。例如,如果我做了这样的事情:

  1. p.stdin.write("1\n5\n")
  2. errors = getOutput(errQueue)
  3. output = getOutput(outQueue)


有办法做我想做的事吗?
该脚本将在Linux机器上运行。我修改了我的脚本,删除了universal_newlines=True +将bufsize设置为1,并在写入后立即刷新标准输入。仍然没有得到任何输出。

第二次尝试:

我尝试了这个方法,它对我很有效:

  1. from subprocess import Popen, PIPE
  2. fw = open("tmpout", "wb")
  3. fr = open("tmpout", "r")
  4. p = Popen("./a.out", stdin = PIPE, stdout = fw, stderr = fw, bufsize = 1)
  5. p.stdin.write("1\n")
  6. out = fr.read()
  7. p.stdin.write("5\n")
  8. out = fr.read()
  9. fw.close()
  10. fr.close()

rt4zxlrg

rt4zxlrg1#

目前的答案对我来说都不起作用。最后,我得到了这个工作:

  1. import subprocess
  2. def start(executable_file):
  3. return subprocess.Popen(
  4. executable_file,
  5. stdin=subprocess.PIPE,
  6. stdout=subprocess.PIPE,
  7. stderr=subprocess.PIPE)
  8. def read(process):
  9. return process.stdout.readline().decode("utf-8").strip()
  10. def write(process, message):
  11. process.stdin.write(f"{message.strip()}\n".encode("utf-8"))
  12. process.stdin.flush()
  13. def terminate(process):
  14. process.stdin.close()
  15. process.terminate()
  16. process.wait(timeout=0.2)
  17. process = start("./dummy.py")
  18. write(process, "hello dummy")
  19. print(read(process))
  20. terminate(process)

字符串
使用此dummy.py脚本进行测试:

  1. #!/usr/bin/env python3.6
  2. import random
  3. import time
  4. while True:
  5. message = input()
  6. time.sleep(random.uniform(0.1, 1.0)) # simulates process time
  7. print(message[::-1])


注意事项是(所有这些都在功能中管理):

  • 输入/输出总是带换行符的行。
  • 每次写入后刷新子对象的标准输入。
  • 使用child的标准输出中的readline()

在我看来,这是一个非常简单的解决方案(不是我的,我在这里找到的:https://eli.thegreenplace.net/2017/interacting-with-a-long-running-child-process-in-python/)。

展开查看全部
qc6wkl3g

qc6wkl3g2#

在Linux上有两种解决方案:
第一个是使用一个文件来同时写入输出和从中读取:

  1. from subprocess import Popen, PIPE
  2. fw = open("tmpout", "wb")
  3. fr = open("tmpout", "r")
  4. p = Popen("./a.out", stdin = PIPE, stdout = fw, stderr = fw, bufsize = 1)
  5. p.stdin.write("1\n")
  6. out = fr.read()
  7. p.stdin.write("5\n")
  8. out = fr.read()
  9. fw.close()
  10. fr.close()

字符串
第二,正如J.F.塞巴斯蒂安所提供的,使用fnctl模块使p.stdout和p.stderr管道不阻塞:

  1. import os
  2. import fcntl
  3. from subprocess import Popen, PIPE
  4. def setNonBlocking(fd):
  5. """
  6. Set the file description of the given file descriptor to non-blocking.
  7. """
  8. flags = fcntl.fcntl(fd, fcntl.F_GETFL)
  9. flags = flags | os.O_NONBLOCK
  10. fcntl.fcntl(fd, fcntl.F_SETFL, flags)
  11. p = Popen("./a.out", stdin = PIPE, stdout = PIPE, stderr = PIPE, bufsize = 1)
  12. setNonBlocking(p.stdout)
  13. setNonBlocking(p.stderr)
  14. p.stdin.write("1\n")
  15. while True:
  16. try:
  17. out1 = p.stdout.read()
  18. except IOError:
  19. continue
  20. else:
  21. break
  22. out1 = p.stdout.read()
  23. p.stdin.write("5\n")
  24. while True:
  25. try:
  26. out2 = p.stdout.read()
  27. except IOError:
  28. continue
  29. else:
  30. break

展开查看全部
jxct1oxe

jxct1oxe3#

这是一个交互式shell。你必须在单独的线程上运行read(),否则它会阻塞write()。

  1. import sys
  2. import os
  3. import subprocess
  4. from subprocess import Popen, PIPE
  5. import threading
  6. class LocalShell(object):
  7. def __init__(self):
  8. pass
  9. def run(self):
  10. env = os.environ.copy()
  11. p = Popen('/bin/bash', stdin=PIPE, stdout=PIPE, stderr=subprocess.STDOUT, shell=True, env=env)
  12. sys.stdout.write("Started Local Terminal...\r\n\r\n")
  13. def writeall(p):
  14. while True:
  15. # print("read data: ")
  16. data = p.stdout.read(1).decode("utf-8")
  17. if not data:
  18. break
  19. sys.stdout.write(data)
  20. sys.stdout.flush()
  21. writer = threading.Thread(target=writeall, args=(p,))
  22. writer.start()
  23. try:
  24. while True:
  25. d = sys.stdin.read(1)
  26. if not d:
  27. break
  28. self._write(p, d.encode())
  29. except EOFError:
  30. pass
  31. def _write(self, process, message):
  32. process.stdin.write(message)
  33. process.stdin.flush()
  34. shell = LocalShell()
  35. shell.run()

字符串

展开查看全部
zxlwwiss

zxlwwiss4#

据我所知,最简单的方法是在两端创建专用线程:两个用于父进程上的stdout/stderr,一个用于子进程上的stdin。

相关问题