subprocess.Popen()的使用

subprocess模块主要用于创建子进程,并连接它们的输入、输出和错误管道,获取它们的返回状态。通俗地说就是通过这个模块,你可以在Python的代码里执行操作系统级别的命令,比如“ipconfig”、“du -sh”等等。subprocess模块替代了一些老的模块和函数,比如:`os.system,os.spawn,os.popen*`

subprocess.Popen()的基本入参介绍
subprocess.Popen()(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, timeout=None, check=False, encoding=None, errors=None)
功能:执行args参数所表示的命令,等待命令结束,并返回一个CompletedProcess类型对象。
注意,run()方法返回的不是我们想要的执行结果或相关信息,而是一个CompletedProcess类型对象。
上面参数表里展示的只是一些常用的,真实情况还有很多。
args:表示要执行的命令。必须是一个字符串,字符串参数列表。

stdin、stdout和stderr:子进程的标准输入、输出和错误。其值可以是subprocess.PIPE、subprocess.DEVNULL、一个已经存在的文件描述符、已经打开的文件对象或者None。subprocess.PIPE表示为子进程创建新的管道。subprocess.DEVNULL表示使用os.devnull。默认使用的是None,表示什么都不做。另外,stderr可以合并到stdout里一起输出。

timeout:设置命令超时时间。如果命令执行时间超时,子进程将被杀死,并弹出TimeoutExpired异常。

check:如果该参数设置为True,并且进程退出状态码不是0,则弹出CalledProcessError异常。

encoding:如果指定了该参数,则stdin、stdout和stderr可以接收字符串数据,并以该编码方式编码。否则只接收bytes类型的数据。

shell:如果该参数为True,将通过操作系统的shell执行指定的命令

subprocess.Popen()交互式输入

import subprocess
# 进入shell
s = subprocess.Popen("adb shell  ", stdout=subprocess.PIPE, stdin=subprocess.PIPE)
# 再次输入指令ps -A
s.stdin.write(b"ps -A")
# 注意一定要关闭输入,反否则无法进行读取
s.stdin.close()
# 输出每行的数据
for i in s.stdout.readlines():
    print(i)

以上需要注意的是使用s.stdin.write()之后一定要关闭输入,否则会无法进行读取

实时打印手机日志

ti = time.time() + 60
    os.popen(f"adb -s {device_id} shell logcat -c")
    time.sleep(2)
    # 开始抓取日志
    ps = subprocess.Popen("adb -s {}  shell logcat -v threadtime".format(device_id),stdout=subprocess.PIPE,stdin=subprocess.PIPE)


    time.sleep(1)
    # 启动app
    d.app_start(package_name)
    # time.sleep(1)
    if (package_name=="com.vmos.pro"):
        d(resourceId="com.vmos.pro:id/iv_vm_action").click()
    while True:
        string=ps.stdout.readline().decode("gbk",  "ignore")
        # print(string)
        if (yemian1 in string) :
            d11 = re.search("[0-9]{2}-[0-9]{2} ([0-9]{2}:){2}[0-9]{2}.[0-9]{3}",string)[0]
            d1 = datetime.datetime.strptime(f'{d11}', '%m-%d %H:%M:%S.%f')
            print(d1, "页面1启动")
        elif (yemian2 in string):
            d22 = re.search("[0-9]{2}-[0-9]{2} ([0-9]{2}:){2}[0-9]{2}.[0-9]{3}",string)[0]
            d2 = datetime.datetime.strptime(f'{d22}', '%m-%d %H:%M:%S.%f')
            print(d2, "页面2启动")
            break
        elif ti<time.time():
            print("启动超时")
            # 假如是因为广告超时则点击广告后重试
            if (package_name == "com.f1player") or (package_name == "com.x8zs.sandbox"):
                if d.xpath(
                        '//*[@resource-id="com.byted.pangle:id/tt_reward_full_frame_top"]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]').wait(
                    0.5):
                    print("出现广告")
                    d(text="| 跳过").click(timeout=30)
                    d(resourceId="com.byted.pangle:id/tt_video_ad_close_layout").click()
            ps.kill()
            d.app_stop(package_name)
            assert 0
    ps.kill()
    # os.system('taskkill /t /f /pid {}'.format(ps.pid))
    # 计算时间
    d3 = d2 - d1
    # 打印时间  取小数点后2位并转换位浮点数
    d4 = (float(str(d3)[5:10]))
    print(d4)
    return d4

以上是根据日志获取app启动时间的计算,这里值得注意的是,每次获取到跳出循环之后一定要把把创建的进程给杀掉(比如上面使用了ps.kill()),不然后台会一直残留这个进程,但是假如subprocess.Popen(shell=Ture)的话使用常规的kill()方法是无法杀掉的,因为使用了 shell=True,结束 process 之后,logcat子进程并不会被结束。所以可以使用 os.system(‘taskkill /t /f /pid {}’.format(ps.pid)) 的方式来杀掉子进程

你可能感兴趣的:(python,fiddler,前端,测试工具)