在python中执行shell脚本时,常使用的函数有os.system、 os.popen和subprocess.Popen
os.system( )
该函数的语法为:
os.system(cmd)
参数
cmd:要执行的命令
该函数返回命令执行结果的返回值,system()函数在执行过程中进行了以下三步操作:
1.fork一个子进程;
2.在子进程中调用exec函数去执行命令;
3.在父进程中调用wait(阻塞)去等待子进程结束。 对于fork失败,system()函数返回-1。
由于使用该函数经常会莫名其妙地出现错误,但是直接执行命令并没有问题,所以一般建议不要使用。官方建议使用subprocess模块来生成新进程并获取结果是更好的选择。
例1:
测试脚本,该脚本的路径为:F:\python\Test_demo.py
import os
File_path = input("请输入文件路径:")
def get_file_name(path):
File_Name = os.path.basename(path)
File_Dirname = os.path.dirname(path)
print(File_Name,File_Dirname)
return File_Name, File_Dirname
def main():
get_file_name(File_path)
if __name__ == "__main__":
main()
例1_1:
import os
result = os.system("F:\python\\Test_demo.py")
print(result)
"""
上面代码的输出结果为:
请输入文件路径:F:\python\test_file
test_file F:\python
0
"""
例2:
import os
retcode = os.system('ping -n 2 -w 3 192.168.1.104')
if retcode == 1:
print ("Success" )
else:
print (" Fail" )
#上面代码的输出结果为:Success
注意:
1、使用os.system()用来执行cmd指令后返回结果为1表示执行成功,返回0表示失败。
2、os.system()是简单粗暴的执行cmd指令,如果想获取在cmd输出的内容,是没办法获到的
3、os.system()调用外部系统命令,返回命令结果码,但是无法获取命令执行输出结果
os.popen( )
该函数的语法如下:
popen(cmd, mode='r', buffering = -1)
参数:
cmd:要执行的命令。
mode:打开文件的模式,默认为'r',用法与open()相同。
buffering:(可选参数)0意味着无缓冲;1意味着行缓冲;其它正值表示使用参数大小的缓冲。负的bufsize意味着使用系统的默认值
1、这个方法会打开一个管道,返回结果是一个连接管道的文件对象,该文件对象的操作方法同open(),可以从该文件对象中读取返回结果。如果执行成功,不会返回状态码,如果执行失败,则会返回错误信息。这里官方也表示subprocess模块已经实现了更为强大的subprocess.Popen()方法。
2、如果想获取控制台输出的内容,那就用os.popen() 的方法了,popen返回的是一个file对象,跟open打开文件一样操作了,"r"是以读的方式打开
例1:
测试脚本,该脚本的路径为:F:\python\Test_demo.py
print("hello")
例2:
import os
# popen返回文件对象,跟open操作一样
f = os.popen(r"python F:\python\\Test_demo.py", "r")
d = f.read() # 读文件
print(d)
print(type(d))
f.close()
"""上面代码的输出结果为:
hello
<class 'str'>
"""
例3:
在app自动化的时候,经常用到指令:adb devices来判断是否连上了手机,那么问题来了,如何用python代码判断是否正常连上手机?
import os
def Cmd_Command():
# popen返回文件对象,跟open操作一样
cmd = os.popen("adb devices","r") #将返回的结果赋于一变量,便于程序的处理。
Cmd_result = cmd.read()
cmd.close()
print(Cmd_result) # cmd输出结果
# 输出结果字符串处理
s = Cmd_result.split("\n") # 切割换行
new = [x for x in s if x != ''] # 去掉空''
print(new)
# 可能有多个手机设备
devices = [] # 获取设备名称
for i in new:
dev = i.split('\tdevice')
if len(dev) >= 2:
devices.append(dev[0])
if not devices:
print("手机没连上")
else:
print("当前手机设备:%s" % str(devices))
if __name__ == "__main__":
Cmd_Command()
"""
当有设备连接时,输出结果为:
List of devices attached
1f786838 device
['List of devices attached', '1f786838\tdevice']
当前手机设备:['1f786838']
"""
例4:
import os
output = os.popen('ping -n 2 -w 3 192.168.1.104') #返回一个文件对象
final_result = output.read()
output.close()
print(final_result)
"""
正在 Ping 192.168.1.104 具有 32 字节的数据:
请求超时。
请求超时。
192.168.1.104 的 Ping 统计信息:
数据包: 已发送 = 2,已接收 = 0,丢失 = 2 (100% 丢失),
"""
注:
1、os.system(cmd)的返回值只会有0(成功),1,2。返回值是脚本的退出状态码
2、os.popen(cmd)会把执行的cmd的输出作为值返回。返回值是脚本执行过程中的输出内容
3、os.popen()可以实现一个'管道',从这个命令获取的值可以继续被调用。而os.system不同,它只是调用,调用完后自身退出,可能返回个0
4、os.popen()好处在于:将返回的结果赋于一变量,便于程序的处理。
subprocess模块
1、优先介绍subprocess模块的是由于该模块可以替代旧模块的方法,如os.system()、os.popen()等,推荐使用。subporcess模块可以调用外部系统命令来创建新子进程,同时可以连接到子进程的nput/output/error管道上,并得到子进程的返回值。
2、subprocess模块主要有call()、check_call()、check_output()、Popen()函数,简要描述如下:
subprocess.Popen类
该函数的语法如下:
subprocess.Popen(args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags)
参数说明:
args:要调用的外部系统命令。参数args可以是字符串或者序列类型(如:list,元组),用于指定进程的可执行文件及其参数。如果是序列类型,第一个元素通常是可执行文件的路径。
bufsize:默认值为0, 表示不缓存,。为1表示行缓存,。其他正数表示缓存使用的大小,,负数-1表示使用系统默认的缓存大小。
stdin、stdout、stdout:分别表示标准输入、标准输出和标准错误。其值可以为PIPE、文件描述符和None等。默认值为None,表示从父进程继承。
shell
Linux:参数值为False时,Linux上通过调用os.execvp执行对应的程序。为True时,Linux上直接调用系统shell来执行程序。
Windows:参数shell设为true,程序将通过shell来执行。
executable:用于指定可执行程序。一般情况下我们通过args参数来设置所要运行的程序。如果将参数shell设为 True,executable将指定程序使用的shell。在windows平台下,默认的shell由COMSPEC环境变量来指定。
preexec_fn:只在Unix平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用
cwd:设置子进程当前目录
env:env是字典类型,用于指定子进程的环境变量。默认值为None,表示子进程的环境变量将从父进程中继承。
Universal_newlines:不同操作系统下,文本的换行符是不一样的。如:windows下用’/r/n’表示换,而Linux下用 ‘/n’。如果将此参数设置为True,Python统一把这些换行符当作’/n’来处理。
subprocess.PIPE:在创建Popen对象时,subprocess.PIPE可以初始化stdin, stdout或stderr参数,表示与子进程通信的标准流。
subprocess.STDOUT:创建Popen对象时,用于初始化stderr参数,表示将错误通过标准输出流输出。
例1:打印F:\python目录下创建test目录。直接调用进程,不考虑获取调用命令输出内容和结果码
import subprocess
p = subprocess.Popen(args='mkdir test', shell=True, cwd='F:\python')
p.wait()
print("文件夹生成成功")
#执行成功后可看到指定目录下生成指定文件夹
例2:调用ping命令执行,获取命令执行输出内容
import subprocess
p = subprocess.Popen(args='ping 'ping www.baidu.com', stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
p.wait()
print(p.stdout.read())
"""
b'\r\n\xd5\xfd\xd4\xda Ping www.a.shifen.com [180.97.33.108] \xbe\xdf\xd3\xd0 32 \xd7\xd6\xbd\xda\xb5\xc4\xca\xfd\xbe\xdd:\r\n\xc0\xb4\xd7\xd4 180.97.33.108 \xb5\xc4\xbb\xd8\xb8\xb4: \xd7\xd6\xbd\xda=32 \xca\xb1\xbc\xe4=31ms TTL=54\r\n\xc0\xb4\xd7\xd4 180.97.33.108 \xb5\xc4\xbb\xd8\xb8\xb4: \xd7\xd6\xbd\xda=32 \xca\xb1\xbc\xe4=31ms TTL=54\r\n\xc0\xb4\xd7\xd4 180.97.33.108 \xb5\xc4\xbb\xd8\xb8\xb4: \xd7\xd6\xbd\xda=32 \xca\xb1\xbc\xe4=30ms TTL=54\r\n\xc0\xb4\xd7\xd4 180.97.33.108 \xb5\xc4\xbb\xd8\xb8\xb4: \xd7\xd6\xbd\xda=32 \xca\xb1\xbc\xe4=30ms TTL=54\r\n\r\n180.97.33.108 \xb5\xc4 Ping \xcd\xb3\xbc\xc6\xd0\xc5\xcf\xa2:\r\n \xca\xfd\xbe\xdd\xb0\xfc: \xd2\xd1\xb7\xa2\xcb\xcd = 4\xa3\xac\xd2\xd1\xbd\xd3\xca\xd5 = 4\xa3\xac\xb6\xaa\xca\xa7 = 0 (0% \xb6\xaa\xca\xa7)\xa3\xac\r\n\xcd\xf9\xb7\xb5\xd0\xd0\xb3\xcc\xb5\xc4\xb9\xc0\xbc\xc6\xca\xb1\xbc\xe4(\xd2\xd4\xba\xc1\xc3\xeb\xce\xaa\xb5\xa5\xce\xbb):\r\n \xd7\xee\xb6\xcc = 30ms\xa3\xac\xd7\xee\xb3\xa4 = 31ms\xa3\xac\xc6\xbd\xbe\xf9 = 30ms\r\n'
"""
1、说明:p.stdout、p.stdin、p.stderr为文件对象,可以使用文件对象函数,如read()。即要获得输出时需要指定stdout、stdin、stderr参数
2、试了下只指定stdout=subprocess.PIPE时,也能获取输出内容
例3:
import subprocess
child = subprocess.Popen(['adb','devices'],stdout=subprocess.PIPE,shell = True)
print ('parent process')
print(child .stdout.read())
"""
parent process
b'List of devices attached\r\n\r\n'
"""
例4:
import subprocess
order='adb devices' #获取连接设备
pi= subprocess.Popen(order,shell=True,stdout=subprocess.PIPE)
print(pi.stdout.read()) #打印结果
#上面代码的输出结果为:b'List of devices attached\r\n\r\n'
例5:实时的打印出日志信息
import subprocess
order = 'adb logcat'
pi = subprocess.Popen(order, shell=True, stdout=subprocess.PIPE)
for i in iter(pi.stdout.readline, 'b'): #生成一个迭代器
print(i) #此处i是以字节(byte)的形式输出的
i = str(i)
log = i.split("\\") #log类型为列表,字符串处理
log = str(log)
log_txt = open("F:\\log.txt","a") #将输出的log保存到本地txt文件中
log_txt.writelines(log + "\n")
"""
["b'--------- beginning of system", 'r', "n'"]
["b'11-22 16:14:35.078 563 570 I QISL : QSEE Interrupt Service Listener Thread is started", 'r', "n'"]
["b'11-22 16:14:35.078 563 570 I QISL : QSEE Interrupt Service Listener was activated successfully", 'r', "n'"]
["b'11-22 16:14:35.205 576 576 I vold : Vold 3.0 (the awakening) firing up", 'r', "n'"]
["b'11-22 16:14:35.206 576 576 V vold : Detected support for: exfat ext4 f2fs ntfs vfat", 'r', "n'"]
"""
例5_1:
import subprocess
order='adb logcat'
file_out = subprocess.Popen(order, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while True:
line = file_out.stdout.readline()
print(line)
if subprocess.Popen.poll(file_out)==0: #判断子进程是否结束
break
例5_2:
string = "一杯敬故乡,一杯敬远方"
print(string.split(",")) #切片后返回的类型为列表
A,B = string.split(",") #序列解包赋值后类型为str
print(A)
print(type(A))
file =open("F:\\log_1.txt","a")
file.writelines(A)
"""
['一杯敬故乡', '一杯敬远方']
一杯敬故乡
<class 'str'>
"""
注:
1、在subprocess中,可以使用上一个命令执行的输出结果作为下一次执行的输入
2、os.popen()是一次性获取执行的结果,而subprocess.Popen()可以连续性的得到执行的结果(生成一个迭代器)
3、os.popen()获取的输出结果是直接可读的,而subprocess.Popen()获取的输出结果是以二进制形式的且会有换行符
subprocess.call()
函数原型:call(*popenargs, **kwargs)。
call( )调用外部系统命令执行,并返回程序执行结果码。执行成功时返回1,未成功返回0
例1:
import subprocess
retcode = subprocess.call('ping -n 2 -w 3 192.168.1.104', shell=True)
print(retcode)
#上面代码的输出结果为:1
Popen的方法:
Popen.poll()
用于检查子进程是否已经结束。设置并返回returncode属性。
Popen.wait()
等待子进程结束。设置并返回returncode属性。
Popen.communicate(input=None)
与子进程进行交互。向stdin发送数据,或从stdout和stderr中读取数据。可选参数input指定发送到子进程的参数。 Communicate()返回一个元组:(stdoutdata, stderrdata)。
注意:如果希望通过进程的stdin向其发送数据,在创建Popen对象的时候,参数stdin必须被设置为PIPE。同样,如 果希望从stdout和stderr获取数据,必须将stdout和stderr设置为PIPE。
Popen.send_signal(signal)
向子进程发送信号。
Popen.terminate()
停止(stop)子进程。在windows平台下,该方法将调用Windows API TerminateProcess()来结束子进程。
Popen.kill()
杀死子进程。
Popen.stdin
如果在创建Popen对象时,参数stdin被设置为PIPE,Popen.stdin将返回一个文件对象用于策子进程发送指令。否则返回None。
Popen.stdout
如果在创建Popen对象时,参数stdout被设置为PIPE,Popen.stdout将返回一个文件对象用于策子进程发送指令。否则返回 None。
Popen.stderr
如果在创建Popen对象时,参数stdout被设置为PIPE,Popen.stdout将返回一个文件对象用于策子进程发送指令。否则返回 None。
Popen.pid
获取子进程的进程ID。
Popen.returncode
获取进程的返回值。如果进程还没有结束,返回None
注:具体使用方法可以参考https://www.jianshu.com/p/b34cc19840d7