TOC

Python 执行系统命令的方法

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

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

一、commands 模块

os.popen 的封装。

Deprecated since version 2.6: The commands module has been removed in Python 3. Use the subprocess module instead.

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

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

二、os 模块

os.system(command) -> exit_status
os.popen(command [, mode='r' [, bufsize]]) -> pipe

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]

四、Pexpect 模块

待更新...

五、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 模块

  • sh <>
sh.ls('-l')

ls_cmd = sh.Command('ls')
ls_cmd()