在Windows上将Ctrl+C信号发送到Python subprocess子进程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/polyhedronx/article/details/82022093

一、问题描述

在用Python爬取网络视频时,利用了ffmpeg下载并合并m3u8文件,在CMD上运行结果如下:

C:\Users\fz.000>ffmpeg -i "https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone.m3u8?auth_key=1535101557-0-0-fedf56c648bfe0d60ef8e0aa89b6a297&expiration=1535101557&disable_local_cache=0" -c copy "E:/PycharmProjects/Video_Crack/video/video1.mp4"
ffmpeg version N-91646-g78d4b6bd43 Copyright (c) 2000-2018 the FFmpeg developers
  built with gcc 8.2.1 (GCC) 20180813
  configuration: --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --enable-libmfx --enable-amf --enable-ffnvcodec --enable-cuvid --enable-d3d11va --enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynth
  libavutil      56. 19.100 / 56. 19.100
  libavcodec     58. 23.100 / 58. 23.100
  libavformat    58. 17.103 / 58. 17.103
  libavdevice    58.  4.101 / 58.  4.101
  libavfilter     7. 26.100 /  7. 26.100
  libswscale      5.  2.100 /  5.  2.100
  libswresample   3.  2.100 /  3.  2.100
  libpostproc    55.  2.100 / 55.  2.100
[hls,applehttp @ 00000263fedba840] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00001.ts?auth_key=1535101557-0-0-e6986ea2f49dd0a3ad5954823a79b3d0' for reading
[hls,applehttp @ 00000263fedba840] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00002.ts?auth_key=1535101557-0-0-1f6b4fb3db9df8067d972990c71a74cb' for reading
Input #0, hls,applehttp, from 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone.m3u8?auth_key=1535101557-0-0-fedf56c648bfe0d60ef8e0aa89b6a297&expiration=1535101557&disable_local_cache=0':
  Duration: 00:00:59.12, start: 1.433556, bitrate: N/A
  Program 0
    Metadata:
      variant_bitrate : 0
    Stream #0:0: Video: h264 (High) ([27][0][0][0] / 0x001B), yuv420p, 1280x2276, 25 fps, 25 tbr, 90k tbn, 50 tbc
    Metadata:
      variant_bitrate : 0
    Stream #0:1: Audio: aac (LC) ([15][0][0][0] / 0x000F), 44100 Hz, stereo, fltp
    Metadata:
      variant_bitrate : 0
Output #0, mp4, to 'E:/PycharmProjects/Video_Crack/video/video1.mp4':
  Metadata:
    encoder         : Lavf58.17.103
    Stream #0:0: Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1280x2276, q=2-31, 25 fps, 25 tbr, 90k tbn, 90k tbc
    Metadata:
      variant_bitrate : 0
    Stream #0:1: Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp
    Metadata:
      variant_bitrate : 0
Stream mapping:
  Stream #0:0 -> #0:0 (copy)
  Stream #0:1 -> #0:1 (copy)
Press [q] to stop, [?] for help
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00003.ts?auth_key=1535101557-0-0-91103599f3d036c53025d4469811f47f' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00004.ts?auth_key=1535101557-0-0-5425a38189bbdc37cce8e75c99ab8bc0' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00005.ts?auth_key=1535101557-0-0-9b2cfb83e2058f2904be5b9fc3cd2665' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00006.ts?auth_key=1535101557-0-0-7c76c5b983dec9efe285a19beafe7fa4' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00007.ts?auth_key=1535101557-0-0-973c55d77a4dc646c11d79aeba2bac4f' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00008.ts?auth_key=1535101557-0-0-76f76084ff3e5bd058aa7e00336b07d4' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00009.ts?auth_key=1535101557-0-0-82b119c4d5e5e756c4449e5870e18f00' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00010.ts?auth_key=1535101557-0-0-3530886b5ebacb68c63dca9b1b0d31af' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00011.ts?auth_key=1535101557-0-0-22cba48926d6bfedd02711f7da42a577' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00012.ts?auth_key=1535101557-0-0-af00d2111cf04e0d1d81f6523cb3d2ab' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00013.ts?auth_key=1535101557-0-0-64bd48246a8ba362cedfff4a78d1ebfa' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00014.ts?auth_key=1535101557-0-0-b82190c2c1a4ddebeea337688c1a351e' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00015.ts?auth_key=1535101557-0-0-72da7559f7fbe2ec24f30ef0be2cbc50' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00016.ts?auth_key=1535101557-0-0-7c93a14b6c6c10b09d24b30ea4bf7db3' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00017.ts?auth_key=1535101557-0-0-a7ae4f217482020d64e85c2e78704cd2' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00018.ts?auth_key=1535101557-0-0-f74f9fb587e0b96e1a1a053bcb2e511d' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00019.ts?auth_key=1535101557-0-0-21fcb34bb2c06fb165ed29083d278014' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00020.ts?auth_key=1535101557-0-0-36b552cde620d564fe469a5d7138111a' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00021.ts?auth_key=1535101557-0-0-fd86d0f8d8e7815f3da90626f4855233' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00022.ts?auth_key=1535101557-0-0-6cfd887896c6292abb643c8fc6d05535' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00023.ts?auth_key=1535101557-0-0-513a1071888bd474984b9b37345b997c' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00024.ts?auth_key=1535101557-0-0-126f976c659bf9bc55eacb6dfdb0f1e0' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00025.ts?auth_key=1535101557-0-0-1a990e2b3bbb8167c9dc1c3ae4fe644b' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00026.ts?auth_key=1535101557-0-0-9fa465a24d3329a415484e286fbd252d' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00027.ts?auth_key=1535101557-0-0-6cb593e7c453f0d981e3ddc7baff6ace' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00028.ts?auth_key=1535101557-0-0-fbdf9f5f84c52e39c5d973e53eb01f75' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00029.ts?auth_key=1535101557-0-0-95cf329432d5c39f98c8a7554194477e' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00030.ts?auth_key=1535101557-0-0-a601529344546d2b3733e2e6454b475c' for reading
frame= 1479 fps= 58 q=-1.0 Lsize=   15220kB time=00:00:59.11 bitrate=2109.0kbits/s speed=2.33x
video:14263kB audio:924kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.210394%

C:\Users\fz.000>

运行结束后,在相应的目录下就会有下载好的视频。

运行命令后的结果中有这么一句:

也就是说下载过程中按下“q”键会终止下载。用同样的视频m3u8链接尝试了一下,果然还没下载完就立刻停止了,从文件的大小就可以看出来,是比原来要小的,不过这时的视频仍然能够正常播放(这就是后面我们要的效果)。

下面我们不用CMD,而是用python程序实现这个过程:

该程序使用subprocess模块创建并返回一个子进程,并在这个子进程中执行指定的程序(也就是ffmpeg命令)。同时可以进行进程间通信,将子程序的输出信息(也就是采用命令行时的那一大片输出信息)打印出来。

subprocess模块用法的相关用法参见:https://blog.csdn.net/polyhedronx/article/details/82015271

import shlex
import subprocess


if __name__ == '__main__':
	try:
		shell_cmd = 'ffmpeg -i "https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone.m3u8?auth_key=1535101557-0-0-fedf56c648bfe0d60ef8e0aa89b6a297&expiration=1535101557&disable_local_cache=0" -c copy "E:/PycharmProjects/Video_Crack/video/video1.mp4"'
		cmd = shlex.split(shell_cmd)
		p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
		while p.poll() is None:
			line = p.stdout.readline().decode('gbk').strip()
			print(line)
		if p.returncode == 0:
			print('subprocess success')
		else:
			print('subprocess failed with code', p.returncode)
	except (OSError, ValueError):
		print('stop here')

 程序写好之后,我们仍然不满足,想要把它做成一个图形界面,其中需要有三个按钮,“下载”、“停止”和“退出”,如下图。“下载”和“退出”都比较容易,问题就是这个停止按钮,怎么样才能实现按下“停止”按钮视频停止下载,并且已经下载好的部分还能正常播放呢?也就是说我们要写一个函数,按下“停止”按钮会启动该函数,而该函数的功能就是停止视频下载(并且保证已下载的部分能正常播放)。

二、初步尝试

首先,我们很自然的会想到,可以向子进程发送一个“q”,实现CMD中按下“q”的效果,而且查一下资料,的确有向subprocess子进程发送信息的命令:

Popen.communicate(input='something', timeout=None)

# or

Popen.stdin.write('something')

但是...这其实是行不通的,因为这样发送给子进程的是数据而不是命令,效果就像是在CMD上打印了一个“q”,并不能使视频下载停止。

好吧,我们不要放弃,那有没有向子进程发送信号的命令呢?答案是有的:

Popen.send_signal(signal)  # 向子进程发送signal信号

Popen.terminate()  # 终止(stop)子进程

Popen.kill()  # 杀死子进程。在Windows上,kill()和terminate()作用相同

其中,signal信号的相关知识参见:https://blog.csdn.net/polyhedronx/article/details/81989918

通过上面这篇博客,我们知道在Windows中还有两种方法可以分别向进程和进程组发送信号:

os.kill(pid, sid)

os.killpg(pgid, sid)

其中,pid为进程号,比如“Popen.pid”,sid为要发送的信号。Windows中可以使用的信号是有限制的,详见:https://blog.csdn.net/polyhedronx/article/details/81988335

下面两种方法的效果是一样的:

Popen.send_signal(signal.SIGTERM)

os.kill(Popen.pid, signal.SIGTERM)

有了这么多可以用的命令,我们先来尝试一下 terminate() 和 kill() :

import shlex
import subprocess

if __name__ == '__main__':
	try:
		shell_cmd = 'ffmpeg -i "https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone.m3u8?auth_key=1535110216-0-0-3d035cc32139ce884c8a1cbb14d0d04d&expiration=1535110216&disable_local_cache=0" -c copy "E:/PycharmProjects/Video_Crack/video/video1.mp4"'
		cmd = shlex.split(shell_cmd)
		p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
		count = 0
		while p.poll() is None:
			line = p.stdout.readline().decode('gbk').strip()
			print(line)
			count += 1
			if count == 50:
				p.terminate()
				# p.kill()
		if p.returncode == 0:
			print('subprocess success')
		else:
			print('subprocess failed with code', p.returncode)
	except (OSError, ValueError, KeyboardInterrupt):
		print('stop here')

上面程序的意思是打印出50条语句时终止子进程,因为下载完成的话CMD中打印的语句肯定大于50条,也就是我们在它还没有下载完成时就把进程终止了:

从图中可以看出,视频确实没有下载完成,因为下载完成之后视频会有15220KB,这样好像是已经满足我们的要求了,因为我们只要在按下“停止”按钮触发的函数里把子进程kill掉就好了。

但是!!!这个视频是无法正常播放的。。。

也即是说terminate() 和 kill()虽然可以终止视频下载,但也破坏了下载过程,使得下载好的部分无法播放。

三、出现转机

terminate() 和 kill()尝试失败后,现在我们只有下面两条命令可以用了:

Popen.send_signal(signal)

os.kill(Popen.pid, signal)

希望全部寄托在了Windows可用的signal上,通过这篇文章python-subprocess模块用法了解到我们可用下面几种signal:

# SIGTERM信号
Popen.send_signal(signal.SIGTERM)
os.kill(Popen.pid, signal.SIGTERM)

# CTRL_C信号
Popen.send_signal(signal.CTRL_C_EVENT)
os.kill(Popen.pid, signal.CTRL_C_EVENT)

# CTRL_BREAK信号
Popen.send_signal(signal.CTRL_BREAK_EVENT)
os.kill(Popen.pid, signal.CTRL_BREAK_EVENT)

此外,需要注意的是,对于 os.kill(pidsig) ,在python文档中有如下描述:

Windows:signal.CTRL_C_EVENT和signal.CTRL_BREAK_EVENT信号是特殊信号,只能发送到共享公共控制台窗口的控制台进程,例如某些子进程(subprocesses)。 sig的任何其他值都将导致进程被TerminateProcess API无条件地终止,并且退出代码将被设置为sig。 Windows版本的 kill() 还会占用进程句柄。

原文:https://docs.python.org/3.6/library/os.html

对于signal.CTRL_C_EVENT(signal.CTRL_BREAK_EVENT与此类似)python文档有如下描述:

The signal corresponding to the Ctrl+C keystroke event. This signal can only be used with os.kill().

Availability: Windows.

New in version 3.2.

原文:https://docs.python.org/3.6/library/signal.html#signal.CTRL_C_EVENT

也就是说 signal.CTRL_C_EVENT 和 signal.CTRL_BREAK_EVENT 只能用于os.kill()。此外,由于在 Windows 上,SIGTERM和terminate() 的作用相同,所以我们弃用该方法。综上,只剩下了两种方法:

# CTRL_C信号
os.kill(Popen.pid, signal.CTRL_C_EVENT)

# CTRL_BREAK信号
os.kill(Popen.pid, signal.CTRL_BREAK_EVENT)

看到CTRL+C,我们好像明白了什么,这不就是中断程序的命令吗!

先在命令行试一下,下载过程中按下CTRL+C,程序终止,视频也下了一半,最重要的是,视频是可以正常播放的!

这说明程序运行的过程中,我们只要给子进程发送一个Ctrl+C信号,就可以完美达到我们的目的,而Ctrl+C信号也正是我们有的:

# CTRL_C信号
os.kill(Popen.pid, signal.CTRL_C_EVENT)

二话不说,立马在python程序中试一试:

import os
import shlex
import signal
import subprocess

if __name__ == '__main__':
	try:
		shell_cmd = 'ffmpeg -i "https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone.m3u8?auth_key=1535110216-0-0-3d035cc32139ce884c8a1cbb14d0d04d&expiration=1535110216&disable_local_cache=0" -c copy "E:/PycharmProjects/Video_Crack/video/video1.mp4"'
		cmd = shlex.split(shell_cmd)
		p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
		count = 0
		while p.poll() is None:
			line = p.stdout.readline().decode('gbk').strip()
			print(line)
			count += 1
			if count == 50:
				os.kill(p.pid, signal.CTRL_C_EVENT)
		if p.returncode == 0:
			print('subprocess success')
		else:
			print('subprocess failed with code', p.returncode)
	except (OSError, ValueError, KeyboardInterrupt):
		print('stop here')

但是,可能是为了让我深刻认识到理论和实践的差距,果然程序又出了其它幺蛾子:视频并没有停止下载,而是下载完了之后才触发CTRL+C信号。。。

请注意视频的大小是15220KB,也就是完整的下载了整个视频。

当然,我也试了 CTRL_BREAK_EVENT 信号,结果也是下载完视频才触发信号:

四、最终圣战

到现在,所有想到的方法都试了一遍,但是没有一个既能中止视频下载,已经下载好的那部分又能正常播放的。当然,我们的努力还是有收获的,最接近我们最终目的的还是 CTRL_C_EVENT 信号,下面就继续拿他寻找解决办法。

# CTRL_C信号
os.kill(Popen.pid, signal.CTRL_C_EVENT)

好吧,我就不卖关子了,最终我还是找到解决办法,很简单,只要这样就行了:

os.kill(0, signal.CTRL_C_EVENT)

详见:https://stackoverflow.com/questions/7085604/sending-c-to-python-subprocess-objects-on-windows

上程序试一试:

import os
import shlex
import signal
import subprocess

if __name__ == '__main__':
	try:
		shell_cmd = 'ffmpeg -i "https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone.m3u8?auth_key=1535110216-0-0-3d035cc32139ce884c8a1cbb14d0d04d&expiration=1535110216&disable_local_cache=0" -c copy "E:/PycharmProjects/Video_Crack/video/video1.mp4"'
		cmd = shlex.split(shell_cmd)
		p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
		count = 0
		while p.poll() is None:
			line = p.stdout.readline().decode('gbk').strip()
			print(line)
			count += 1
			if count == 50:
				os.kill(0, signal.CTRL_C_EVENT)
		if p.returncode == 0:
			print('subprocess success')
		else:
			print('subprocess failed with code', p.returncode)
	except (OSError, ValueError, KeyboardInterrupt):
		print('stop here')

能看到它能正常播放真是太舒服了,那这个终极方法的原理到底是什么呢?

我们知道,os.kill() 有两个参数,即os.kill(pidsig),如果这两个参数中其中一个为0会是什么情况呢?

(1)os.kill(pid, 0)

若sig为0,则不发送任何信号,但仍执行错误检查;这可用于检查进程ID或进程组ID的存在。

原文链接:https://unix.stackexchange.com/questions/169898/what-does-kill-0-do

(2)os.kill(0, sig)

例如:os.kill(0, signal.CTRL_C_EVENT) ,表示程序通过向cmd窗口中的所有进程发送CTRL_C_EVENT来获取CTRL-C信号。

看一个例子(来源:https://www.programcreek.com/python/example/13966/signal.CTRL_C_EVENT):

def pipe_server():
  ''' Part of attach/set_attach for Windows '''
  while 1:
    pipe = Pipe('vdb_%d' % os.getpid(), server=True)
    knock = pipe.read(3)
    if knock == 'vsi':
      os.kill(0, signal.CTRL_C_EVENT)      
      #ctypes.windll.kernel32.GenerateConsoleCtrlEvent(0, os.getpid())
    pipe.disconnect()
    pipe.close() 

更新(2018/08/28):

有一个小bug,使用os.kill(0, signal.CTRL_C_EVENT)之后,CTRL_C_EVENT信号发送给了当前子进程及其所有相关的父进程,所以我的图形界面也被关闭了!解决方法如下。原理就是接收到CTRL_C_EVENT信号之后会引发KeyboardInterrupt异常,我们捕捉这个异常,并在异常发生之后(此时GUI被关闭了)再次触发GUI,这个过程是很快的,所以我们不会发现GUI关闭过!

class GUIOperate(object):
	# 运行GUI
	@staticmethod
	def gui_loop():
		try:
			top.mainloop()
		except KeyboardInterrupt:
			print('下载中止...')
			GUIOperate.gui_loop()

参考:

python-subprocess模块用法

Python错误:AttributeError: module 'signal' has no attribute 'SIGALRM'

https://docs.python.org/3.6/library/os.html

https://docs.python.org/3.6/library/signal.html#signal.CTRL_C_EVENT

Sending ^C to Python subprocess objects on Windows

GenerateConsoleCtrlEvent function

猜你喜欢

转载自blog.csdn.net/polyhedronx/article/details/82022093