Python3.5+ subprocess用法概括以及使用场景分析
Python是一门很给力的语言,可以完成想要的所有操作,但在极少数情况下,你可能需要调用外部程序。比如Linux命令或者运行Shell脚本。以前写Python脚本的时候,我最排斥的就是调用外部命令,可能因为出了错误不好处理,要么纯用Shell脚本,要么纯用Python脚本,“杂交算什么东西”! 那我现在的想法呢,也很纠结,看下去吧。以前代码最常见的是使用Python的原始方法是使用** os.system
**,但是后来被subprocess模块替代,也是这篇文章所要讲的。
1. 侃侃run()用法
我只用**
subprocess.run()
**,其他的都是垃圾?!
Python中关于多进程协程异步等一些较新用法,官方一直在更新完善其用法,然后趋于稳定。subprocess模块旨在替换几个较旧的模块和功能,比如**os.system
,os.spawn*
。而对于Python3.5 +** **subprocess.run()
**用来代替之前的call等几个老方法,万剑归一。
1.1 示例1:运行命令并获取返回代码
run()的行为与call()的行为大致相同,Python3.5+之后应该放弃使用call(),但是call()方法还是保留着,可以使用
1 |
|
1.2 示例2:运行命令,如果底层进程报错,强制抛出异常
加入check=True
即可
1 |
|
1.3 示例3:流畅使用shell
如果想要正常使用shell命令,只要加入shell=True
1 |
|
如果使用用户输入作为参数来构建命令字符串,加上shell = True的话可能有潜在的安全威胁(代码注入)
1.4 示例4:以字符串形式存储输出和错误消息
如果底层进程返回非零退出代码,则不会出现异常;可以通过CompletedProcess对象中的stderr属性访问错误消息。
不报错的例子:
1 |
|
报错的例子:
1 |
|
命令不存在的例子:
1 |
|
2. 更底层的Popen用法
关于调用子进程,官方的推荐方法是用可以处理所有用例的run(函数。对于更高级的用例,可以直接使用底层的Popen接口。但这个方法在我看来用不太到(个人拙见),上面的run能满足99%的场景,理由我会在下面的使用场景中说出。
这里推荐几篇好的文章,它们都在讲解subprocess的时候讲了popen方法:
1. Python 3 Subprocess Examples
2. subprocess — Spawning Additional Processes
3. How to Execute Shell Commands with Python
3. 使用场景分析与选择
我表达下我个人拙见。
Python能做很多事,大多数脚本都能胜任。但是对于Linux本身的操作,像是重启服务命令,一般系统特有命令,或者是Linux强大的三剑客(sed,awk,grep)在处理文本时有很大的优势,这些Python脚本都不好去代替。
- 对于一些单条的管道命令,Python中最精确的是用Popen命令,但是我们完全可以用类似于run(‘grep python | wc > out’, shell=True)来代替,虽然上面说这不安全,但是写在脚本内部,不把命令作为一个参数输入,是没有问题的。而且写之前肯定是要进行测试的,所以我上面才觉得底层的popen必要性不大。
- 对于一些麻烦的命令集合,类似于部署脚本、驱动类、开启关闭脚本,我们可以采用写shell脚本,把相关的一部分用shell脚本来代替,然后如果要在Python中调用的话只要**
run('./xx.sh',shell=True)
**(或者调皮一点bash中前嵌入运行python脚本),我不知道这优不优雅,有空去查查。
4. 总结
如何在Python中运行外部命令,最有效的方法是使用子进程模块及其提供的所有功能。最值得注意的是,应该考虑使用subprocess.run。
还有其他有用的库支持Python中的shell命令,拓展了一些其他功能,比如与tty终端交互,获取系统CPU信息等,如plumbum,sh,psutils和pexpect,这里不做拓展。
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!