如何在后台 运行 EXE 程序并在 python 中获取输出
How to run an EXE program in the background and get the outuput in python
我想在后台运行一个exe程序
假设程序是 httpd.exe
我可以 运行 它但是当我想要输出时它会卡住,因为如果它成功启动则没有输出。但是如果有错误也没关系。
这是我使用的代码:
import asyncio
import os
os.chdir('c:\apache\bin')
process, stdout, stderr = asyncio.run(run('httpd.exe'))
print(stdout, stderr)
async def run(cmd):
proc = await asyncio.create_subprocess_exec(
cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE)
stdout, stderr = await proc.communicate()
return (proc, stdout, stderr)
我试图使以下代码尽可能通用:
- 我不假设 运行 程序是只将其输出单独写入 stdout 还是单独写入 stderr。因此,我通过启动两个线程来捕获两个输出,每个线程一个,然后将输出写入一个可以实时读取的公共队列。当在每个 stdout 和 stderr 上遇到 end-of-stream 时,线程将一个特殊的
None
记录写入队列以指示流结束。所以队列的 reader 知道在看到两个这样的“流结束”指示器后,将不再有行写入队列并且进程已经有效结束。
- 可以使用参数 shell=True 调用
subprocess.Popen
这样也可以 built-in shell命令,也使命令的规范更容易(它现在可以是单个字符串而不是字符串列表)。
- 函数
run_cmd
returns创建进程和队列。您现在只需要循环读取队列中的行,直到看到两个 None
记录。一旦发生这种情况,您就可以等待该过程完成,这应该是立竿见影的。
- 如果您知道您正在启动的进程只将其输出写入 stdout 或 stderr(或者如果您只想捕获这些输出之一),那么您可以修改程序以仅启动一个线程并指定
subprocess.PIPE
仅这些输出之一的值,然后从队列中读取行的循环应该只寻找一个 None
end-of-stream 指标。
- 线程是 daemon 线程,因此如果您希望根据在检测到所有 end-of-stream 记录之前已读取的进程输出终止,然后线程将与主进程一起自动终止。
run_apache
,其中运行s Apache 作为一个子进程,本身就是一个守护线程。如果它检测到来自 Apache 的任何输出,它会设置一个已传递给它的事件。启动 run_apache
的主线程可以定期测试此事件,等待此事件,等待 run_apache
线程结束(只有在 Apache 结束时才会发生)或者可以通过全局变量终止 Apache proc
.
import subprocess
import sys
import threading
import queue
def read_stream(f, q):
for line in iter(f.readline, ''):
q.put(line)
q.put(None) # show no more data from stdout or stderr
def run_cmd(command, run_in_shell=True):
"""
Run command as a subprocess. If run_in_shell is True, then
command is a string, else it is a list of strings.
"""
proc = subprocess.Popen(command, shell=run_in_shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
q = queue.Queue()
threading.Thread(target=read_stream, args=(proc.stdout, q), daemon=True).start()
threading.Thread(target=read_stream, args=(proc.stderr, q), daemon=True).start()
return proc, q
import os
def run_apache(event):
global proc
os.chdir('c:\apache\bin')
proc, q = run_cmd(['httpd.exe'], False)
seen_None_count = 0
while seen_None_count < 2:
line = q.get()
if line is None:
# end of stream from either stdout or stderr
seen_None_count += 1
else:
event.set() # Seen output line:
print(line, end='')
# wait for process to terminate, which should be immediate:
proc.wait()
# This event will be set if Apache write output:
event = threading.Event()
t = threading.Thread(target=run_apache, args=(event,), daemon=True)
t.start()
# Main thread runs and can test event any time to see if it has done any output:
if event.is_set():
...
# The main thread can wait for run_apache thread to normally terminate,
# will occur when Apache terminates:
t.join()
# or the main thread can kill Apache via global variable procL
proc.terminate() # No need to do t.join() since run_apache is a daemon thread
我想在后台运行一个exe程序
假设程序是 httpd.exe
我可以 运行 它但是当我想要输出时它会卡住,因为如果它成功启动则没有输出。但是如果有错误也没关系。
这是我使用的代码:
import asyncio
import os
os.chdir('c:\apache\bin')
process, stdout, stderr = asyncio.run(run('httpd.exe'))
print(stdout, stderr)
async def run(cmd):
proc = await asyncio.create_subprocess_exec(
cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE)
stdout, stderr = await proc.communicate()
return (proc, stdout, stderr)
我试图使以下代码尽可能通用:
- 我不假设 运行 程序是只将其输出单独写入 stdout 还是单独写入 stderr。因此,我通过启动两个线程来捕获两个输出,每个线程一个,然后将输出写入一个可以实时读取的公共队列。当在每个 stdout 和 stderr 上遇到 end-of-stream 时,线程将一个特殊的
None
记录写入队列以指示流结束。所以队列的 reader 知道在看到两个这样的“流结束”指示器后,将不再有行写入队列并且进程已经有效结束。 - 可以使用参数 shell=True 调用
subprocess.Popen
这样也可以 built-in shell命令,也使命令的规范更容易(它现在可以是单个字符串而不是字符串列表)。 - 函数
run_cmd
returns创建进程和队列。您现在只需要循环读取队列中的行,直到看到两个None
记录。一旦发生这种情况,您就可以等待该过程完成,这应该是立竿见影的。 - 如果您知道您正在启动的进程只将其输出写入 stdout 或 stderr(或者如果您只想捕获这些输出之一),那么您可以修改程序以仅启动一个线程并指定
subprocess.PIPE
仅这些输出之一的值,然后从队列中读取行的循环应该只寻找一个None
end-of-stream 指标。 - 线程是 daemon 线程,因此如果您希望根据在检测到所有 end-of-stream 记录之前已读取的进程输出终止,然后线程将与主进程一起自动终止。
run_apache
,其中运行s Apache 作为一个子进程,本身就是一个守护线程。如果它检测到来自 Apache 的任何输出,它会设置一个已传递给它的事件。启动run_apache
的主线程可以定期测试此事件,等待此事件,等待run_apache
线程结束(只有在 Apache 结束时才会发生)或者可以通过全局变量终止 Apacheproc
.
import subprocess
import sys
import threading
import queue
def read_stream(f, q):
for line in iter(f.readline, ''):
q.put(line)
q.put(None) # show no more data from stdout or stderr
def run_cmd(command, run_in_shell=True):
"""
Run command as a subprocess. If run_in_shell is True, then
command is a string, else it is a list of strings.
"""
proc = subprocess.Popen(command, shell=run_in_shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
q = queue.Queue()
threading.Thread(target=read_stream, args=(proc.stdout, q), daemon=True).start()
threading.Thread(target=read_stream, args=(proc.stderr, q), daemon=True).start()
return proc, q
import os
def run_apache(event):
global proc
os.chdir('c:\apache\bin')
proc, q = run_cmd(['httpd.exe'], False)
seen_None_count = 0
while seen_None_count < 2:
line = q.get()
if line is None:
# end of stream from either stdout or stderr
seen_None_count += 1
else:
event.set() # Seen output line:
print(line, end='')
# wait for process to terminate, which should be immediate:
proc.wait()
# This event will be set if Apache write output:
event = threading.Event()
t = threading.Thread(target=run_apache, args=(event,), daemon=True)
t.start()
# Main thread runs and can test event any time to see if it has done any output:
if event.is_set():
...
# The main thread can wait for run_apache thread to normally terminate,
# will occur when Apache terminates:
t.join()
# or the main thread can kill Apache via global variable procL
proc.terminate() # No need to do t.join() since run_apache is a daemon thread