python:通过python执行shell脚本

在python中执行shell脚本时,常使用的函数有os.systemos.popensubprocess.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    

猜你喜欢

转载自blog.csdn.net/qq_39314932/article/details/84329180
今日推荐