gpt4 book ai didi

python - 从 python 子进程获取输出并向其发出命令

转载 作者:太空宇宙 更新时间:2023-11-03 11:09:41 25 4
gpt4 key购买 nike

我正在尝试从子进程获取输出,然后根据前面的输出向该进程发出命令。当程序需要进一步输入时,我需要执行此操作的次数不定。 (如果可能的话,我还需要能够隐藏子进程命令提示符)。

我认为这将是一项简单的任务,因为我已经看到这个问题在 2003 年的帖子中被讨论过,现在快到 2012 年了,它似乎是一个非常普遍的需求,而且看起来它应该是任何编程语言。显然我错了,几乎 9 年后,仍然没有以稳定、非破坏性、平台独立的方式完成这项任务的标准方法!

我不太了解文件 I/O 和缓冲或线程,所以我更喜欢尽可能简单的解决方案。如果有实现此功能的模块兼容python 3.x,我会非常愿意下载它。我意识到有多个问题基本上问的是同一件事,但我还没有找到解决我试图完成的简单任务的答案。

这是我目前基于各种来源的代码;但是我完全不知道下一步该做什么。我所有的尝试都以失败告终,有些尝试使用了我 100% 的 CPU(基本上什么都不做)并且不会退出。

import subprocess
from subprocess import Popen, PIPE
p = Popen(r'C:\postgis_testing\shellcomm.bat',stdin=PIPE,stdout=PIPE,stderr=subprocess.STDOUT shell=True)
stdout,stdin = p.communicate(b'command string')

如果我的问题不清楚,我将发布示例批处理文件的文本,它演示了需要向子进程发送多个命令的情况(如果您键入不正确的命令字符串,程序将循环)。

@echo off
:looper
set INPUT=
set /P INPUT=Type the correct command string:
if "%INPUT%" == "command string" (echo you are correct) else (goto looper)

如果有人能帮助我,我将不胜感激,我相信其他人也会如此!

这里编辑的是使用 eryksun 代码的功能代码(下一篇文章):

import subprocess
import threading
import time
import sys

try:
import queue
except ImportError:
import Queue as queue

def read_stdout(stdout, q, p):
it = iter(lambda: stdout.read(1), b'')
for c in it:
q.put(c)
if stdout.closed:
break

_encoding = getattr(sys.stdout, 'encoding', 'latin-1')
def get_stdout(q, encoding=_encoding):
out = []
while 1:
try:
out.append(q.get(timeout=0.2))
except queue.Empty:
break
return b''.join(out).rstrip().decode(encoding)

def printout(q):
outdata = get_stdout(q)
if outdata:
print('Output: %s' % outdata)

if __name__ == '__main__':
#setup
p = subprocess.Popen(['shellcomm.bat'], stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
bufsize=0, shell=True) # I put shell=True to hide prompt
q = queue.Queue()
encoding = getattr(sys.stdin, 'encoding', 'utf-8')

#for reading stdout
t = threading.Thread(target=read_stdout, args=(p.stdout, q, p))
t.daemon = True
t.start()

#command loop
while p.poll() is None:
printout(q)
cmd = input('Input: ')
cmd = (cmd + '\n').encode(encoding)
p.stdin.write(cmd)
time.sleep(0.1) # I added this to give some time to check for closure (otherwise it doesn't work)

#tear down
for n in range(4):
rc = p.poll()
if rc is not None:
break
time.sleep(0.25)
else:
p.terminate()
rc = p.poll()
if rc is None:
rc = 1

printout(q)
print('Return Code: %d' % rc)

然而,当脚本从命令提示符运行时,会发生以下情况:

C:\Users\username>python C:\postgis_testing\shellcomm7.py
Input: sth
Traceback (most recent call last):
File "C:\postgis_testing\shellcomm7.py", line 51, in <module>
p.stdin.write(cmd)
IOError: [Errno 22] Invalid argument

程序似乎在从命令提示符运行时关闭。有什么想法吗?

最佳答案

此演示使用专用线程从标准输出读取。如果您四处搜索,我相信您可以找到用面向对象的接口(interface)编写的更完整的实现。至少我可以说这对我在 Python 2.7.2 和 3.2.2 中提供的批处理文件都有效。

shellcomm.bat:

@echo off
echo Command Loop Test
echo.
:looper
set INPUT=
set /P INPUT=Type the correct command string:
if "%INPUT%" == "command string" (echo you are correct) else (goto looper)

这是我根据命令“错误”、“仍然错误”和“命令字符串”的顺序得到的输出:

Output:
Command Loop Test

Type the correct command string:
Input: wrong
Output:
Type the correct command string:
Input: still wrong
Output:
Type the correct command string:
Input: command string
Output:
you are correct

Return Code: 0

为了读取管道输出,readline 有时可能会起作用,但批处理文件中的 set/P INPUT 自然不会写入行结束符。因此,我改为使用 lambda: stdout.read(1) 一次读取一个字节(效率不高,但可以)。读取函数将数据放在队列中。主线程在写入命令后从队列中获取输出。在此处的 get 调用上使用超时使其等待一小段时间以确保程序正在等待输入。相反,您可以检查输出中的提示以了解程序何时需要输入。

总而言之,您不能指望像这样的设置可以普遍工作,因为您尝试与之交互的控制台程序可能会在管道传输时缓冲其输出。在 Unix 系统中,有一些可用的实用命令,您可以将它们插入管道以将缓冲修改为非缓冲、行缓冲或给定大小——例如 stdbuf。 .还有一些方法可以让程序认为它连接到一个 pty(参见 pexpect)。但是,如果您无法访问程序的源代码以使用 setvbuf 显式设置缓冲,我不知道在 Windows 上解决此问题的方法。 .

import subprocess
import threading
import time
import sys

if sys.version_info.major >= 3:
import queue
else:
import Queue as queue
input = raw_input

def read_stdout(stdout, q):
it = iter(lambda: stdout.read(1), b'')
for c in it:
q.put(c)
if stdout.closed:
break

_encoding = getattr(sys.stdout, 'encoding', 'latin-1')
def get_stdout(q, encoding=_encoding):
out = []
while 1:
try:
out.append(q.get(timeout=0.2))
except queue.Empty:
break
return b''.join(out).rstrip().decode(encoding)

def printout(q):
outdata = get_stdout(q)
if outdata:
print('Output:\n%s' % outdata)

if __name__ == '__main__':

ARGS = ["shellcomm.bat"] ### Modify this

#setup
p = subprocess.Popen(ARGS, bufsize=0, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
q = queue.Queue()
encoding = getattr(sys.stdin, 'encoding', 'utf-8')

#for reading stdout
t = threading.Thread(target=read_stdout, args=(p.stdout, q))
t.daemon = True
t.start()

#command loop
while 1:
printout(q)
if p.poll() is not None or p.stdin.closed:
break
cmd = input('Input: ')
cmd = (cmd + '\n').encode(encoding)
p.stdin.write(cmd)

#tear down
for n in range(4):
rc = p.poll()
if rc is not None:
break
time.sleep(0.25)
else:
p.terminate()
rc = p.poll()
if rc is None:
rc = 1

printout(q)
print('\nReturn Code: %d' % rc)

关于python - 从 python 子进程获取输出并向其发出命令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8285032/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com