当前位置: 移动技术网 > IT编程>脚本编程>Python > 《自拍教程44》adb命令_一键截取Logcat日志

《自拍教程44》adb命令_一键截取Logcat日志

2020年03月18日  | 移动技术网IT编程  | 我要评论

郭秀仪,品牌护肤品批发,山西万荣第二小学女班主任

​本篇文章难度较大,阅读时间较长,历劫进阶。。。

android系统测试或app测试过程中,
有的android终端设备由于磁盘太小,未开启随系统自启动logcat序列log,
即未实时在后台截取logcat log,所以需要测试人员进行手动截取logcat log,
这种情况下, 一般我们是直接cmd, adb logcat -v threadtime > d:\logcat.txt,
通过以上命令来进行手动截取logcat 日志。
我曾经考虑过一种更方便快捷的截取一键截取logcat的python脚本,
直接点击运行该python脚本即可,无需手动敲以上命令!
本周重点介绍的是通过是subprocess.popen()类进行命令行的调用,销毁(杀进程)!


准备阶段
  1. adb logcat -v threadtime > d:\logcat_20200310_101112.txt可以打印按线程时间log并保存到一个文件。
  2. 由于adb logcat命令是一个持续输出的命令,
    它如果没被销毁(杀进程),会一直持续截取下去。
  3. os.system()函数和os.popen()函数并不适合做子进程销毁,
    这时候就要考虑更高端的subprocess.popen()类来实现进程销毁。

subprocess.popen()类介绍

python官方推荐使用subprocess.popen()类来调起其他命令(即创建子进程),
其具备优质的子进程管理(创建,销毁,进行标准输入,进行标准输出,进行错误输出等)特性。
为什么说他是一个类,而不是一个函数,请看源码截图:

既然是类,则首先先看下这个类的初始化的源代码:

    def __init__(self, args, bufsize=-1, executable=none,
                 stdin=none, stdout=none, stderr=none,
                 preexec_fn=none, close_fds=true,
                 shell=false, cwd=none, env=none, universal_newlines=none,
                 startupinfo=none, creationflags=0,
                 restore_signals=true, start_new_session=false,
                 pass_fds=(), *, encoding=none, errors=none, text=none):


我们只需要了解以下几个初始化的参数即可:

参数 释义 参数初始化举例
args 需要子进程执行的命令或者命令数组 args="adb reboot"
args=["adb", "reboot"]
stdin standard input
对某个子进程的标准输入对象进行定义
如果我们的子进程后续是不需要进行交互式输入的:
stdin=none (默认值)
如果我们的子进程是需要进行交互式输入的:
stdin=subprocess.pipe
stdin=none

stdin=subprocess.pipe
stdout standard output
对某个子进程的标准输出进行定义。
如果我们不需要捕捉标准输出则:stdout=none(默认值)
如果需要对标准输出进行捕捉及分析:stdout=subprocess.pipe
如果需要对标准输出进行存储:stdout=open("log.txt","wb")

stdout=none

stdout=subprocess.pipe

stdout=open("log.txt","wb")
stderr standard error
对某个子进程的标准异常错误提示信息进行定义,
如果我们不需要捕异常错误信息则:stderr=none(默认值)
如果需要对异常错误进行捕捉及分析:stderr=subprocess.pipe
如果需要对异常错误进行存储:stderr=open("log.txt","wb")

stderr=none

stderr=subprocess.pipe

stderr=open("log.txt","wb")
shell 如果子进程是必须通过cmd /c
或者/system/bin/sh -c 来执行的,则必须shell=true。
我们一般的命令,请直接用shell=false(默认值)即可。

比如如果在windows上执行adb命令,
如果shell=true, 将额外调用起cmd进程,然后是adb进程
如果shell=false, 将只会调用起adb进程(建议用这个)

shell=false

shell=true


是类,则肯定需要实例化成对象后才能进行具体的操作,
且是类,则就涉及类的属性(变量)定义,方法(函数)定义,
我们假定初始化后的对象名是p_obj, 释义如下:

属性(变量)列表 释义
p_obj.pid 子进程对象的:进程pid
p_obj.returncode 子进程对象的:当前的状态
none:代表子进程命令还在执行中,无法获取其状态
0:代表正常执行了子进程命令并正常结束
1:代表异常结束,比如被kill或者terminate了
p_obj.args 子进程对象执行的:具体命令


方法(函数)列表 释义
方法(函数)列表 释义
p_obj.kill() windows系统上,与terminate()功能相同,都是杀掉进程
linux系统上,发送的是sigtkill信号,即强制杀掉你这个进程,类似kill -9

万能建议用法(顺序执行以下2个函数):
p_obj.terminate()
p_obj.kill()
p_obj.terminate() windows系统上,与terminate()功能相同,都是杀掉进程
linux系统上,发送的是sigterm信号,即通知子进程赶紧自行结束,类似kill -15

万能用法(顺序执行以下2个函数):
p_obj.terminate()
p_obj.kill()


python批处理脚本形式
# coding=utf-8
import os
import subprocess
from datetime import datetime

command = "adb logcat -v threadtime"

now_time = datetime.now().strftime("%y%m%d_%h%m%s")
log_file = "logcat_" + now_time + ".txt"

hf = open("%s" % log_file, "wb")
# 因为没指定具体路径,默认就是在当前脚本运行的路径下创建这个log_file

# 开始执行adb 命令
p_obj = subprocess.popen(
        args=command,
        stdin=none, stdout=hf,
        stderr=hf, shell=false)

print("logcat catching...")
# 持续询问是否需要停止截取
judgement = input("if you think it is enough, please input y:")
while (judgement != "y" and judgement != "y"):  ###这里必须是and.
    print("invalid input, please input y")
    judgement = input("if you think it is enough, please input y:")
else:
    if (judgement == "y" or judgement == "y"):  # 如果收到停止yes信号,则开始结束截取
        p_obj.terminate()
        p_obj.kill()

hf.close()
print("logcat log has saved to %s" % os.path.abspath(log_file))
os.system("pause")


python面向过程函数形式

本次优化,将command作为常量,常量一般是编码风格是全部大写。
涉及文件open读写的,尽量用with形式。

# coding=utf-8
import os
import subprocess
from datetime import datetime

command = "adb logcat -v threadtime"


def catch_logcat():
    now_time = datetime.now().strftime("%y%m%d_%h%m%s")
    log_file = "logcat_" + now_time + ".txt"
    # 因为没指定具体路径,默认就是在当前脚本运行的路径下创建这个log_file

    # 开始执行adb 命令
    with open("%s" % log_file, "wb") as hf:
        p_obj = subprocess.popen(
                args=command,
                stdin=none, stdout=hf,
                stderr=hf, shell=false)

        print("logcat catching...")
        # 持续询问是否需要停止截取
        judgement = input("if you think it is enough, please input y:")
        while (judgement != "y" and judgement != "y"):  ###这里必须是and.
            print("invalid input, please input y")
            judgement = input("if you think it is enough, please input y:")
        else:
            if (judgement == "y" or judgement == "y"):  # 如果收到停止yes信号,则开始结束截取
                p_obj.terminate()
                p_obj.kill()

    return os.path.abspath(log_file)


print("logcat log has saved to %s" % catch_logcat())
os.system("pause")


python面向对象形式

本次优化:

  1. 在类的初始化的时候,把必须要做的事做完。
    logcatcatcher()类初始化不需要任何参数输入,但是需要新建2个属性,
    且必须在初始化的时候把这2个属性新建好。
  2. 非必要暴露给其他模块的函数(或属性),尽量考虑私有化,加_即可,比如_create_hf(self)函数,就是私有函数。
# coding=utf-8
import os
import subprocess
from datetime import datetime

command = "adb logcat -v threadtime"  # 常量


class logcatcathcher():
    def __init__(self):
        self.log_file = none
        self.hf = none
        self._create_hf()
        self.p_obj = subprocess.popen(
                args=command,
                stdin=none, stdout=self.hf,
                stderr=self.hf, shell=false)

    def _create_hf(self):
        now_time = datetime.now().strftime("%y%m%d_%h%m%s")
        self.log_file = "logcat_" + now_time + ".txt"
        # 因为没指定具体路径,默认就是在当前脚本运行的路径下创建这个log_file
        self.hf = open("%s" % self.log_file, "wb")

    def catch_logcat(self):
        print("logcat catching...")
        # 持续询问是否需要停止截取
        judgement = input("if you think it is enough, please input y:")
        while (judgement != "y" and judgement != "y"):  ###这里必须是and.
            print("invalid input, please input y")
            judgement = input("if you think it is enough, please input y:")
        else:
            if (judgement == "y" or judgement == "y"):  # 如果收到停止yes信号,则开始结束截取
                self.p_obj.terminate()
                self.p_obj.kill()
                self.hf.close()  # 关闭文件句柄
        return os.path.abspath(self.log_file)


if __name__ == '__main__':
    l_obj = logcatcathcher()
    print("logcat log has saved to %s" % l_obj.catch_logcat())
    os.system("pause")


代码运行方式

确保android车机设备通过usb线与电脑连接了,adb设备有效连接,
以上代码的3种实现形式都可以直接运行,比如保存为catch_logcat.py并放在桌面,
建议python catch_logcat.py运行,当然也可以双击运行。
运行效果如下:


碰到复杂的源代码,不要着急,慢慢地按以上方法去剖析,
非常有必要查看python官方的subprocess模块文档或源代码上的注释文档;
如果还是不懂,可以看python关于subprocess的源码(c:\users
\administrator\appdata\local\programs\python\python37-32\lib\subprocess.py),
如果想知道别人是怎么调用的,可以用findstr或者grep搜下python37-32整个文件夹下,
有多少源代码的模块调用了subprocess.popen,看别人是怎么调用的。
如果自己在调用的方法上没问题,但是报错了,可以去www.bing.com上搜下代码错误的原因,
方法总是比困难多,各位加油!


更多更好的原创文章,请访问官方网站:
自拍教程(自动化测试python教程,武散人编著)
原文链接:
也可关注“武散人”微信订阅号,随时接受文章推送。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网