前言
什么是外部程序呢?比如你常用的adb则为外部程序,我们通过Python程序调用adb,对于当前的Python程序,则为调用外部程序adb,我相信你应该用过os.system()、os.popen()等方式调用adb,而Python官方则推荐subprocess模块中的run()函数,你可以根据自己的喜好,决定使用哪种方式。
在正式讲解前,我要先罗列一些前置知识点,如果你还不知道以下术语,建议私下再补习一下,这样再来读我这篇文章,才能丝滑起来!!!这些术语有:
1、标准输入
2、标准输出
3、标准错误
4、进程间同步
5、父进程、子进程
6、等待子进程、不等待子进程
7、信号
8、程序
9、进程树
10、退出状态码
11、命令
不熟悉没关系,慢慢的就都熟悉了,开始我们今天的分享,Python调用外部程序的9种方式
一、os.system()
使用os模块下的system()函数,传入参数为具体的命令,如下
import os
os.system("adb devices")
print("haha,我会等上面的外部程序adb执行完毕")
输出
List of devices attached
haha,我会等上面的外部程序adb执行完毕
特点共有6个
1、与父进程共享标准输出
List of devices attached 是 adb devices的标准输出,外部程序在子进程中的标准输出,会和python当前主进程的标准输出放置在一起输出,从进程角度看,子进程共享了父进程的标准输出!!
2、阻塞父进程(如果从父进程的角度来看,也可称为父进程等待子进程运行结束)
子进程执行外部命令(程序)时,会阻塞当前python进程(父进程)的运行,所以你看到输出中:haha,我会等上面的外部程序adb执行完毕的输出,python脚本程序作为父进程会等待子进程中的adb程序执行完毕后才会继续执行
3、返回退出状态码
os.system()函数根据平台不同(类Linux或者Windows),返回值表示退出状态码
4、无法获取子进程的标准输出
无法从程序中获取到外部程序的标准输出,不方便我们在程序处理,比如上面adb devies的例子中,List of devices attached这个字符串我们在程序中是拿不到的!os.system()只有能到退出状态码
5、无法使用shell特性(特别注意)
完全绕开了bash程序,与当前bash无关,而是直接执行了adb程序,所以如果你在命令中包含管道符、重定向,这些bash解释器程序使用语法特性,是完全不可以的。比如 adb devices | grep xxx,这样肯定不行,因为它并没有bash,管道符号|,是不认识的
6、标准错误也一起输出
标准错误会和父进程中的输出在一起,即屏幕上
二、os.popen()
os模块还有一个popen()函数,可传入3个参数,后两个参数可省略
第一个参数表示传入的命令
第二个参数表示模式(r表示读管道、w表示写管道)
第三个参数表示管道缓冲区的大小
返回值是一个类似file的对象,即os模块下的_wrap_close类的对象,每个返回值对象代表的是连接到管道的文件对象(niubility)
import os
file_like = os.popen("adb devices")
print(file_like.read())
print("haha,我会等上面的程序执行完毕")
特点共有4个
1、底层实现为subprocess的Popen
内部使用subprocess.Popen 实现
2、可获取子进程的标准输出,此时会阻塞父进程(注意:如果没有调用read()方法,将不会等待子进程结束)
由于返回值的是一个代表管道的文件对象,read()方法可以获取外部程序的标准输出,返回字符串,方便我们获取值在程序中进一步操作
3、返回值为代表管道的文件对象
返回值为代表管道的文件对象,还有一个readlines()方法,自动以换行符作为分隔符,返回一个包含所有标准输出的list,每行字符串为list中的一个元素
4、可以获取退出状态码
waitstatus_to_exitcode() 方法可以将代表管道的文件对象中的 close()方法的返回值转为退出状态码
三、subprocess.getoutput()
import subprocess
output = subprocess.getoutput("adb devices")
print(output)
print("等待外部程序执行结束")
subprocess模块下getoutput()函数,传入参数为命令
特点共计3个
1、内部使用subprocess.getstatusoutput()实现
2、返回值为子进程的标准输出与标准错误(官方文档显示会有标准错误,不知道哪个版本更新了)
可以很方便的在程序中获取外部程序的标准输出与标准
3、阻塞父进程
同样会阻塞python父进程的运行,它会等待外部程序的标准输出,即等待子进程执行结束
subprocess.getoutput(cmd, *, encoding=None, errors=None)
Return output (stdout and stderr) of executing cmd in a shell.
Like getstatusoutput(), except the exit code is ignored and the return value is a string containing the command’s output. Example:
>>>
>>> subprocess.getoutput('ls /bin/ls') '/bin/ls'Availability: Unix, Windows.
Changed in version 3.3.4: Windows support added
New in version 3.11: Added encoding and errors arguments.
四、subprocess.getstatusoutput()
import subprocess
output = subprocess.getstatusoutput("adb devices")
print(output)
print("等待外部程序执行结束")
传入参数为要执行的命令
特点共计3个
1、内部使用subprocess.check_output()
2、返回值为元组
(0, 'List of devices attached\n'),第一个元素表示外部程序的退出状态码、第二个元素代表标准输出,实用吧
3、阻塞父进程(即:父进程等待子进程中的外部程序运行结束
阻塞python主进程的运行,直到拿到外部程序的标准输出,即等待子进程执行结束
五、subprocess.check_output()
import subprocess
output = subprocess.check_output("adb devices")
print(output)
print("等待外部程序执行结束")
传入参数为命令
特点共计4个
1、内部实现,使用的subprocess.run()
2、返回值是字节串对象(注意:不是字符串对象)
由于返回的不是字符串对象,需要自行转换为字符串对象,这点尤其注意
3、便于我们写程序时的灵活定制
可以在程序中控制标准错误、外部程序的执行时间、增加标准输入、是否使用bash等选项(注意:默认情况下并没有使用bash解释器)
4、退出状态码非0时,会抛出异常为CalledProcessError
退出状态码非0时,会抛出异常为CalledProcessError,我们可以选择处理该异常,作为外部程序执行出错时的方案,这个就很实用了,如果你的程序需要对外部程序进行校验,重试,用它就对了
六、subprocess.run()
import subprocess
output = subprocess.run("adb devices")
print(output)
print("等待外部程序执行结束")
输出
List of devices attached
CompletedProcess(args='adb devices', returncode=0)
等待外部程序执行结束
传入参数为命令
特点共计5个
1、内部使用subprocess.Popen类,每个Popen对象代表子进程
2、默认外部程序的标准输出,使用python进程器主进程的标准输出
3、默认返回为CompletedProcess对象
4、更灵活的控制子进程中执行的程序,标准输入、标准输出、标准错误、退出状态码等等随便拿着用
高度定制化需求时,就可以使用这个来玩了
5、阻塞当前父进程,即父进程会等待子进程执行结束
七、subprocess.Popen()
import subprocess
child = subprocess.Popen("adb devices")
print(child)
print("等待外部程序执行结束")
输出
<Popen: returncode: None args: ['a', 'd', 'b', ' ', 'd', 'e', 'v', 'i', 'c',...>
等待外部程序执行结束
List of devices attached
特点
1、最大程度的控制子进程中执行外部程序的过程,越来越手动了……
2、替代os模块在子进程中执行程序
3、返回的是Popen对象
4、默认不阻塞父进程的运行,即默认情况下父进程不等待子进程中的外部程序运行结束,需要等待,显式调用child.wait()
5、标准输出与父进程共用
八、subprocess.call()
import subprocess
output = subprocess.call("adb devices")
print(output)
print("等待外部程序执行结束")
特点
1、返回值为退出状态码
2、同样会等待子进程执行程序结束
九、subprocess.check_call()
import subprocess
output = subprocess.check_call("adb devices")
print(output)
print("等待外部程序执行结束")
特点
1、底层实现依赖subprocess.call()
2、返回值为退出状态码,非0时返回CalledProcessError对象,相当于自动校验外部程序的退出状态码
总结
1、官方提供这么多的执行外部程序方式,首先与标准的制定有关,每个方式都不完美,但总有适合你需求的方式,如果你需要精确编写一些业务逻辑就用subprocess.Popen,如果你只想看退出状态码,则也可以使用仅返回退出状态码的方式,所以还是取决于你的需求
2、大部分方式会阻塞当前进程,但也有特例,比如subprocess.Popen,需要显式调用wait()方法,比如os.popen,如果不调用read()方法,也不会阻塞父进程的执行
3、官方建议使用subprocess模块下的执行方式,已经不建议使用os模块下的执行方式
4、subprocess模块的源码值得一读
5、还有其他调用外部程序的方式,等待你去发掘
6、两个思路:不阻塞当前父进程的执行流,可以采取开启新的线程(进程)去等待外部程序的执行,另一种则是采用subprocess.Popen,干脆不等待子进程的执行流!
7、你明白了嘛?不明白的话,补充一下我开头说那些知识点吧!!!
文章详细介绍了在Python中调用adb命令的多种方式,包括os.system(),os.popen(),subprocess.getoutput(),subprocess.getstatusoutput(),subprocess.check_output()以及subprocess.run()等方法,分析了各自的特点,如标准输出的处理、是否阻塞父进程、获取退出状态码的能力等,并强调了官方推荐使用subprocess模块以及各种方式的适用场景。
5638

被折叠的 条评论
为什么被折叠?



