TOC

Python 执行系统命令的方法

这里是指在本地执行命令的方法,通过 SSH 的方式这里不做讨论。
Update @ 2019-09-09: Python: 通过 SSH 执行命令

声明:直接运行外部命令往往是一种简单的方案,其缺点是其开销相对较大。如果在一个有一定规模的线上服务,应该尽量避免如此设计。

commands 模块

os.popen 的封装。

注意:该模块在 Python3 中已经移除。

commands.getoutput(cmd: str) -> (status: int, output: str)
commands.getstatusoutput(cmd: str) -> output: str
commands.getstatus(file) -> output: str

os 模块

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

os.execl(path, arg0, arg1, ...)
os.execle(path, arg0, arg1, ..., env)
os.execlp(file, arg0, arg1, ...)
os.execlpe(file, arg0, arg1, ..., env)
os.execv(path, args)
os.execve(path, args, env)
os.execvp(file, args)
os.execvpe(file, args, env)

需要特别注意的点

如果不需要输出,os.system 是非常易于使用的。

但是 os.system 会直接执行命令,如果里面有来自用户输入内容的话,就有注入的风险,这是一个非常严重的安全问题,需要非常小心。如果是一个长期项目,依赖开发者小心,不是怎么靠谱。

subprocess 模块

如果加上 shell=True 参数, 也会将命令放到子 shell 中执行,如果输入的是字符串命令,而不是 list 风格,还是会有和 os.system 相同的注入风险。详情可以参考官方文档:17.1.1.1. Frequently Used Arguments
如果不是非常有必要,务必保证 shell=False (默认)!

Python2:

subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False)
subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False)
subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False)

Python3:

subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False,
    cwd=None, timeout=None, **other_popen_kwargs)
subprocess.check_call(args, *, stdin=None, stdout=None, stderr=None, shell=False,
    cwd=None, timeout=None, **other_popen_kwargs)
subprocess.check_output(args, *, stdin=None, stderr=None, shell=False,
    cwd=None, encoding=None, errors=None, universal_newlines=None,
    timeout=None, text=None, **other_popen_kwargs)

# Python3.5 加入的新方法
subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None,
    capture_output=False, shell=False,
    cwd=None, timeout=None, check=False, encoding=None, errors=None,
    text=None, env=None, universal_newlines=None, **other_popen_kwargs)

然后就是底层 API —— Popen 了,其使用相对复杂,能实现更多控制,下次单讲吧。简单使用的时候可以不管。
Update @ 2017-10-09: Subprocess Popen

示例

官方引入 .run 方法之后,将其他几个方法打包在一起,称之为 Older high-level API。既然有官方的推荐,肯定是 .run 设计方面更强大、更安全、更高效了。

注意:返回是一个 Object 而不是 (状态码, 输出)

import subprocess
proc = subprocess.run(['ls', '/etc/passwd'], capture_output=True)
print([proc.args, proc.returncode, proc.stdout, proc.stderr, proc.check_returncode()])
# [['ls', '/etc/passwd'], 0, b'/etc/passwd\n', b'', None]

asyncio 模块

Python 3.4 开始引入的 asyncio 模块中有对 subprocess 的异步支持。

coroutine asyncio.create_subprocess_exec(program, *args,
    stdin=None, stdout=None, stderr=None,
    limit=None, **kwds)

coroutine asyncio.create_subprocess_shell(cmd,
    stdin=None, stdout=None, stderr=None,
    limit=None, **kwds)

用法和 subprocess 模块大同小异,详情参考官方文档
Update @ 2019-08-31: AsyncIO 异步执行命令

第三方模块

sh 模块