Python调用外部进程的几种方法

Python调用外部进程的几种方法

4 年前 · 来自专栏 安阳小栈

在Python中调用外部进程是很常见的需求, 比如文件系统操作, 执行其他任务代码等, 刚好在SO上刷到这个问题, 看了一遍答案, 总结出几种常见的方法.

一. os.system('cmd')

这个是以前用的比较多的方法, 这个方法实际上调用的是系统的C函数system(), 命令以字符串的形式传入, 在系统子shell中执行, 和在真实的shell执行并无二致.

优点 : 可以利用shell的管道和重定向等特性来实现比较复杂的命令

import os
# 使用管道获取所有java进程并强制杀掉
os.system('ps -ef|grep java|cut -c 9-15|xargs kill -9')
# 获取当前目录下所有png图片文件名并重定向到png_list.txt中
os.system('ls *.png > png_list.txt')

缺点: 外部进程的输出无法被python代码捕获, system函数只返回一个exit status(不同的系统环境该状态可能不一样), 此外作为命令传入的字符串必须是合格的命令, 否则会出现意想不到的结果

二. os.popen('cmd', mode='r', buffering=-1)

与os.system的不同之处在于os.popen会开辟一个管道并将输出通过一个file like的对象返回给调用者, 类似python中文件操作方法open一样:

import os
with os.popen('ls *.png', mode='r') as res:
    result = res.read()

三. subprocess.Popen()

这个是python自带库subprocess中的Popen类, 作为os.popen的替代, 拥有更多可选参数, 使用如下:

import subprocess
# 当shell=True时, POSIX类系统默认会调用/bin/sh来执行传入的命令
result = subprocess.Popen(['ls', '-l'], shell=True, stdout=subprocess.PIPE).stdout.read()

四. subprocess.call()

这个函数拥有类似Popen类的可选参数, 但是会等待子进程运行完毕再返回一个返回码, 使用如下:

import subprocess
return_code = subprocess.call(['ls', '-l'], shell=True)

五. subprocess.run()

这个函数在python3.5及以后的版本可用, 有几个非常有用的参数可选, 该函数返回的是一个CompletedProcess对象, 通过该对象可以获取标准输入和输出, 使用示例如下:

import subprocess
# 在shell中执行并获取标准输出和错误
result = subprocess.run(['ls', '-l'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print(result.stdout)
print(result.stderr)
# 对返回值进行检查, 如果子程序返回错误那么抛出一个CalledProcessError的异常
result = subprocess.run(['ls', '-l'],check=True)
# 给子程序加上超时限制, 如果超时, 向上抛出TimeoutExpired异常, 在项目中控制外部进程用时是比较好的习惯
result = subprocess.run(['sleep', '5'], timeout=3)

六. 使用fabric的operations

from fabric import operations
res = operations.local('ls -l', capture=True)
# 在远程服务器上执行命令, 但需要提前配置好登陆信息
res = operations.run('cmd')

七. 第三方模块sh

这是一个很强大的第三方库, 其原理是通过动态解析$PATH来执行二进制命令, 执行并不是python类或函数, 而是用python包装了系统PATH中的可执行命令, 也就是说系统路径中所有命令都是可以执行的. 使用示例:

# 安装很简单
pip install sh
# 执行各种系统命令
sh.ls('-l', '/data')
sh.ifconfig()
# 使用管道, 并获取返回值
count = sh.wc(sh.ls('-l'), '-l')
# 甚至可以使用子命令