Python执行系统命令的几种方法
问题引出
假定面临一个需求,需要在Python程序中调用系统命令/其它可执行程序完成一些事项(下载文件) ,那么有哪些方法可以实现需求?一个示例的 代码结构如下:
class CMDExecutor:
def __init__(self, executable_path):
self.executable = executable_path
def execute(self, params=None):
"""
执行系统命令
:param command:
:return:
"""
if paramsis None:
params= self.__params
# TODO: 执行系统命令
pass
方法一:通过os.system执行系统命令
根据官方文档对os.system的介绍,
os.system()通过调用标准 C 函数 system() 来实现在子进程中执行命令(字符串)。
对 sys.stdin 的更改等不会反映在所执行命令的环境中。
如果 command 生成了任何输出,它将被发送到解释器的标准输出流。
C 标准没有指明这个 C 函数返回值的含义,因此这个 Python 函数的返回值取决于具体系统。
在 Unix 上,返回值为进程的退出状态,以针对 wait() 而指定的格式进行编码。
在 Windows 上,返回值是运行 command 后系统 Shell 返回的值。该 Shell 由 Windows
环境变量 COMSPEC: 给出:通常是 cmd.exe,它会返回命令的退出状态。
具体操作方式示例1:
os.system(" ".join([self.executable, params]))
效果:在命令正确的情况下,os.system会启动一个子进程,并阻塞父进程,直到子进程返回结果。
具体操作方式示例2:如果想要避免主进程被os.system阻塞,可以使用windows的start命令开启一个cmd窗口,代码如下 :
result = os.system(" ".join(["start", self.executable, params]))
但方式2也有问题,如果命令错误(如通过下载器下载文件,指定的url有误),则cmd窗口会挂起(空操作),要实现完整的程序,需要考虑跟踪该cmd子进程并杀死空操作的cmd进程。
方法二:os.popen
新建一个子进程,在子进程中执行命令,并创建一个管道用于父进程和子进程之间的通信。父进程可以从管道读消息或者向管道写消息。
与os.system的明显不同是popen方便父子进程交互,默认不阻塞父进程,除非父进程要从 子进程中读信息
p= os.popen(" ".join([self.executable, command]))
print(p.name)
p.read() # 会阻塞父进程
效果简介:非阻塞方式启用子进程执行命令
方法三:subprocess.Popen()
根据官方文档描述,subprocess
模块允许你生成新的进程,连接它们的输入、输出、错误管道,并且获取它们的返回码。此模块打算代替一些老旧的模块与功能:
os.system
os.spawn*
就功能而言,subprocess.Popen模块允许我们更方便地跟踪子进程的状态
具体使用方式示例 :
p = subprocess.Popen(" ".join([self.executable, command]), shell=False, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
p的常用属性和方法包括:
pid: 子进程PID。需要注意如果使用Popen构造函数时将shell参数设置为True,则会启用一个系统终端,默认返回的子进程id是该终端进程的id,而不是在该终端上所执行命令启用的子进程id。
kill(): 杀死子进程。
因此若要杀死通过Popen创建的子进程,只需要调用p.kill()或者调用os模块下的方法,把进程id和信号作为参数传递给kill方法即可。
os.kill(p.pid, signal.SIGTERM)
备注:SIGTERM和SIGKILL都是终止进程的信号,前者较为温和(可能 被进程捕获并 忽略),后者是强制终止信号,会杀死子进程,拒绝抵抗。
参考阅读
- 调试时遇到一些问题:调用os.killpg(p.pid, signal.SIGUSR1)
报错:AttributeError: module ‘signal’ has no attribute ‘SIGUSR1’
原因:参考issue
"windows doesn't have the same SIGUSR signals. After a quick search, the suggestion
from other sources was to revert to using SIGTERM when running on windows."