zwo*_*wol 22 python windows subprocess
我有一个测试工具(用Python编写),需要通过发送来关闭被测程序(用C语言编写)^C.在Unix上,
proc.send_signal(signal.SIGINT)
Run Code Online (Sandbox Code Playgroud)
工作得很好.在Windows上,会抛出错误("不支持信号2"或类似的东西).我正在使用Python 2.7 for Windows,所以我觉得我应该能够做到
proc.send_signal(signal.CTRL_C_EVENT)
Run Code Online (Sandbox Code Playgroud)
但这根本不起作用.我需要做什么?这是创建子进程的代码:
# Windows needs an extra argument passed to subprocess.Popen,
# but the constant isn't defined on Unix.
try: kwargs['creationflags'] = subprocess.CREATE_NEW_PROCESS_GROUP
except AttributeError: pass
proc = subprocess.Popen(argv,
stdin=open(os.path.devnull, "r"),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
**kwargs)
Run Code Online (Sandbox Code Playgroud)
小智 12
通过使用包装器(如提供的链接Vinay中所述),可以使用Windows 启动命令在新的控制台窗口中启动解决方案.
包装器代码:
#wrapper.py
import subprocess, time, signal, sys, os
def signal_handler(signal, frame):
time.sleep(1)
print 'Ctrl+C received in wrapper.py'
signal.signal(signal.SIGINT, signal_handler)
print "wrapper.py started"
subprocess.Popen("python demo.py")
time.sleep(3) #Replace with your IPC code here, which waits on a fire CTRL-C request
os.kill(signal.CTRL_C_EVENT, 0)
Run Code Online (Sandbox Code Playgroud)
捕获CTRL-C的程序代码:
#demo.py
import signal, sys, time
def signal_handler(signal, frame):
print 'Ctrl+C received in demo.py'
time.sleep(1)
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
print 'demo.py started'
#signal.pause() # does not work under Windows
while(True):
time.sleep(1)
Run Code Online (Sandbox Code Playgroud)
启动包装器,例如:
PythonPrompt> import subprocess
PythonPrompt> subprocess.Popen("start python wrapper.py", shell=True)
Run Code Online (Sandbox Code Playgroud)
您需要添加一些IPC代码,允许您控制包装器触发os.kill(signal.CTRL_C_EVENT,0)命令.我在我的应用程序中使用了套接字.
说明:
Preinformation
send_signal(CTRL_C_EVENT)
不起作用,因为CTRL_C_EVENT
只是为了os.kill
.[REF1]os.kill(CTRL_C_EVENT)
将信号发送到当前cmd窗口中运行的所有进程[REF2]Popen(..., creationflags=CREATE_NEW_PROCESS_GROUP)
不起作用,因为CTRL_C_EVENT
进程组被忽略.[REF2]
这是python文档中的一个错误[REF3]实施解决方案
有用的帖子是:
我不得不删除链接前面的http,因为我是新用户,不允许发布两个以上的链接.
更新:基于IPC的CTRL-C Wrapper
在这里,您可以找到一个自写的python模块,提供CTRL-C包装,包括基于套接字的IPC.语法与子进程模块非常相似.
用法:
>>> import winctrlc
>>> p1 = winctrlc.Popen("python demo.py")
>>> p2 = winctrlc.Popen("python demo.py")
>>> p3 = winctrlc.Popen("python demo.py")
>>> p2.send_ctrl_c()
>>> p1.send_ctrl_c()
>>> p3.send_ctrl_c()
Run Code Online (Sandbox Code Playgroud)
码
import socket
import subprocess
import time
import random
import signal, os, sys
class Popen:
_port = random.randint(10000, 50000)
_connection = ''
def _start_ctrl_c_wrapper(self, cmd):
cmd_str = "start \"\" python winctrlc.py "+"\""+cmd+"\""+" "+str(self._port)
subprocess.Popen(cmd_str, shell=True)
def _create_connection(self):
self._connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._connection.connect(('localhost', self._port))
def send_ctrl_c(self):
self._connection.send(Wrapper.TERMINATION_REQ)
self._connection.close()
def __init__(self, cmd):
self._start_ctrl_c_wrapper(cmd)
self._create_connection()
class Wrapper:
TERMINATION_REQ = "Terminate with CTRL-C"
def _create_connection(self, port):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('localhost', port))
s.listen(1)
conn, addr = s.accept()
return conn
def _wait_on_ctrl_c_request(self, conn):
while True:
data = conn.recv(1024)
if data == self.TERMINATION_REQ:
ctrl_c_received = True
break
else:
ctrl_c_received = False
return ctrl_c_received
def _cleanup_and_fire_ctrl_c(self, conn):
conn.close()
os.kill(signal.CTRL_C_EVENT, 0)
def _signal_handler(self, signal, frame):
time.sleep(1)
sys.exit(0)
def __init__(self, cmd, port):
signal.signal(signal.SIGINT, self._signal_handler)
subprocess.Popen(cmd)
conn = self._create_connection(port)
ctrl_c_req_received = self._wait_on_ctrl_c_request(conn)
if ctrl_c_req_received:
self._cleanup_and_fire_ctrl_c(conn)
else:
sys.exit(0)
if __name__ == "__main__":
command_string = sys.argv[1]
port_no = int(sys.argv[2])
Wrapper(command_string, port_no)
Run Code Online (Sandbox Code Playgroud)
尝试GenerateConsoleCtrlEvent
使用调用函数ctypes
.在创建新进程组时,进程组ID应与pid相同.所以,像
import ctypes
ctypes.windll.kernel32.GenerateConsoleCtrlEvent(0, proc.pid) # 0 => Ctrl-C
Run Code Online (Sandbox Code Playgroud)
应该管用.
更新:你是对的,我错过了那部分细节.这里有一篇文章提出了一个可能的解决方案,尽管它有点笨拙.更多细节在这个答案中.
我的解决方案还涉及一个包装脚本,但它不需要IPC,因此使用起来要简单得多。
包装器脚本首先将自身与任何现有控制台分离,然后附加到目标控制台,然后归档 Ctrl-C 事件。
import ctypes
import sys
kernel = ctypes.windll.kernel32
pid = int(sys.argv[1])
kernel.FreeConsole()
kernel.AttachConsole(pid)
kernel.SetConsoleCtrlHandler(None, 1)
kernel.GenerateConsoleCtrlEvent(0, 0)
sys.exit(0)
Run Code Online (Sandbox Code Playgroud)
初始进程必须在单独的控制台中启动,以便 Ctrl-C 事件不会泄漏。例子
p = subprocess.Popen(['some_command'], creationflags=subprocess.CREATE_NEW_CONSOLE)
# Do something else
subprocess.check_call([sys.executable, 'ctrl_c.py', str(p.pid)]) # Send Ctrl-C
Run Code Online (Sandbox Code Playgroud)
我将包装脚本命名为ctrl_c.py
.
归档时间: |
|
查看次数: |
21827 次 |
最近记录: |