欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

《自拍教程52》Python_adb运行Shell脚本

程序员文章站 2022-06-15 09:00:42
Android作为一款Linux终端,肯定是支持.sh后缀的Shell脚本的运行的, 有时候测试环境准备或者长时间截取复杂的日志等,开发会给到一些Shell脚本。 Shell脚本的执行的优势: 1. 快捷高效,Shell脚本是Linux终端都支持的。 2. 由于执行及测试结果都在Linux终端内部存 ......

android作为一款linux终端,肯定是支持.sh后缀的shell脚本的运行的,
有时候测试环境准备或者长时间截取复杂的日志等,开发会给到一些shell脚本。
shell脚本的执行的优势:

  1. 快捷高效,shell脚本是linux终端都支持的。
  2. 由于执行及测试结果都在linux终端内部存储,不会出现因为反复通过usb与windows电脑进行输入输出导致的android系统的i/o cpu消耗过大。

如何通过python来运行shell脚本呢?
何为高端地用python运行shell脚本,这里边的学问可不小,
以下案例,我们写了一个top.sh脚本, 用于持续截取系统各app的cpu占用率情况。
持续截取cpu占用率数据,也是app性能测试(资源消耗)的常用测试方法。


shell脚本的三种运行方式
具体命令 具体效果
adb shell sh top.sh (低端) 执行在前台,即“阻塞”在前台运行,会让你在这个界面等着。
拔掉usb线后,shell脚本自动停止执行
adb shell sh top.sh &(中端) 执行在后台,即“不阻塞”,让后台执行了,不需要你等着执行完。
拔掉usb线后,shell脚本自动停止执行
adb shell nohup sh top.sh &(高端) 独立执行在后台,执行后,即使你拔掉usb线,shell脚本依旧后台运行。

本案例用第三种高端方式来实现



shell脚本与python文件相互存在的两种方式
融合方式 具体效果
显露式(低端) top.sh脚本和python脚本都是独立的文件,top.sh显露在外
---run_top_sh.py
---top.sh
隐藏式(高端) 将top.sh以文本的形式隐藏在python代码中
---run_top_sh.py

显露式的方法,肯定是adb push top.sh /sdcard/top.sh,
本案例脚本用第二种隐藏式高端方式来实现, 具体如何实现呢?



准备阶段

第1步: 将shell脚本以字符串变量的形式存放于python代码块里
第2步: 将shell脚本写入一个临时文件(注意shell脚本是需要运行在linux,linux的行尾符是\n)
第3步: 将以上临时文件adb push 到/sdcard/cpu.sh
第4步: 用adb shell nohup sh /sdcard/cpu.sh & 的方式实现长时间截取,即使usb不小心掉了,也不影响shell脚本继续在后执行截取top。

python批处理脚本形式

记住批处理脚本的精髓:批量顺序执行语句

# coding=utf-8

import os
import tempfile

# 将top.sh的shell脚本copy过来,作为字符串变量
top_sh = '''#!/bin/sh
    while true
    do
        top -n 1 >> /sdcard/top.log
        echo -e "$(date +%y-%m-%d_%h:%m:%s)" >> /sdcard/top.log
        sleep 3
    done
    '''

print("正在生成shell脚本的临时文件......")
signal, temp_file = tempfile.mkstemp()  # 创建一个临时文件
with open(temp_file, 'w+', newline="\n") as hf:  # 将支付按此转成shell脚本,重点注意换行符"\n"
    for line in top_sh:
        hf.write(line)

os.system("adb root") #必要的root
os.system("adb remount")
os.system("adb wait-for-device")
os.system("adb push %s /sdcard/top.sh" % temp_file)  # 推top.sh脚本到终端设备
os.system("adb shell chmod 777 /sdcard/top.sh")  # 赋值777
os.popen("adb shell nohup sh /sdcard/top.sh &")  # 独立后台无干扰执行,popen不阻塞
print("/sdcard/top.sh 脚本后台无干扰运行中......")

print("清除临时文件......")
os.close(signal)  # 临时文件清理
os.remove(temp_file)  # 临时文件清理

os.system("pause")

python面向过程函数形式

面向过程函数的编程思维应该是这样的:
你需要多少个功能(函数),才能做成这个事。
最好把功能(函数)都尽量封装好,只暴露一定的参数接口即可。

import os
import tempfile

# 将top.sh的shell脚本copy过来,作为字符串变量
top_sh = '''#!/bin/sh
    while true
    do
        top -n 1 >> /sdcard/top.log
        echo -e "$(date +%y-%m-%d_%h:%m:%s)" >> /sdcard/top.log
        sleep 3
    done
    '''


def generate_shell(shell_script):
    print("正在生成shell脚本的临时文件......")
    signal, temp_file = tempfile.mkstemp()  # 创建一个临时文件
    with open(temp_file, 'w+', newline="\n") as hf:  # 将支付按此转成shell脚本,重点注意换行符"\n"
        for line in shell_script:
            hf.write(line)
    return signal, temp_file


def clear_tempfile(signal, temp_file):
    print("清除临时文件......")
    os.close(signal)
    os.remove(temp_file)


def push_run_shell(sh_file, push_path):
    os.system("adb root") #必要的root
    os.system("adb remount")
    os.system("adb wait-for-device")
    os.system("adb push %s %s" % (sh_file, push_path))  # 推top.sh脚本到终端设备
    os.system("adb shell chmod 777 %s" % push_path)  # 赋值777
    os.popen("adb shell nohup sh %s &" % push_path)  # 独立后台无干扰执行,popen不阻塞
    print("%s 脚本后台无干扰运行中......"%push_path)


signal, temp_file = generate_shell(top_sh)
push_run_shell(temp_file, "/sdcard/top.sh")
clear_tempfile(signal, temp_file)

os.system("pause")

python面向对象类形式

面向对象类的编程思维应该是这样的:
如果给你一个空白的世界,在这个世界里你需要哪些种类的事物,
这些种类的事物都具备哪些共有的属性与方法,
这些种类(类)的事物(对象),和其他种类(其他类)的事物(其他对象)有什么关系。
尽量把这些类封装好,只暴露对外的属性(变量)和方法(函数)即可。

# coding=utf-8

import os
import tempfile

# 将top.sh的shell脚本copy过来,作为字符串变量
top_sh = '''#!/bin/sh
    while true
    do
        top -n 1 >> /sdcard/top.log
        echo -e "$(date +%y-%m-%d_%h:%m:%s)" >> /sdcard/top.log
        sleep 3
    done
    '''


class shellgeneratorandruner():
    '''generate shell and push into android and run it'''

    def __init__(self, shell_script, push_path):
        self.script_text = shell_script
        self.push_path = push_path
        self.signal = none
        self.temp_file = none

    def generate_shell(self):
        print("正在生产shell脚本的临时文件......")
        self.signal, self.temp_file = tempfile.mkstemp()
        with open(self.temp_file, 'w+', newline="\n") as hf:  # 将支付按此转成shell脚本,重点注意换行符"\n"
            for line in self.temp_file:
                hf.write(line)

    def push_run_shell(self):
        os.system("adb root")   #必要的root
        os.system("adb remount")
        os.system("adb wait-for-device")
        os.system("adb push %s %s" % (self.temp_file, self.push_path))  # 推top.sh脚本到终端设备
        os.system("adb shell chmod 777 %s" % self.push_path)  # 赋值777
        os.popen("adb shell nohup sh %s &" % self.push_path)  # 独立后台无干扰执行,popen不阻塞
        print("%s 脚本后台无干扰运行中......"%self.push_path)


    def clear_tempfile(self):
        os.close(self.signal)
        os.remove(self.temp_file)


if __name__ == '__main__':
    s_obj = shellgeneratorandruner(top_sh, "/sdcard/top.sh")
    s_obj.generate_shell()
    s_obj.push_run_shell()
    s_obj.clear_tempfile()

    os.system("pause")

运行方式与效果

确保android设备通过usb线与电脑连接了,adb设备有效连接,
以上代码的3种实现形式都可以直接运行,比如保存为run_top_sh.py并放在桌面,
建议python run_top_sh.py运行,当然也可以双击运行。
运行效果如下:
其中c:\users\admini~1\appdata\local\temp\tmp5way7qgx这个就是生成的临时文件,
由于一般用户不会涉及到以上临时文件,所以可以实现“无感”地生成shell脚本。
《自拍教程52》Python_adb运行Shell脚本

为什么鼓励用隐藏式来隐藏shell脚本到代码里去,
因为比如后续你写了一个python工具,这个工具用py2exe编译时,
py2exe只能编译打包.py的文件成.exe, 其他的任何非.py的文件无法打包进来,
如果你发布给别人用的时候,也就一个.exe,
大家就觉得你的工具做的比较好,集成的比较好。
相反地,如果你的.exe工具再带一堆的shell脚本,或者其他资源文件,配置文件等,
则相对而言没那么易用,比如容易动不动出现配套文件找不到,
或者被用户随意篡改导致程序无法正常运行,也无法显示你的作品的牛逼。。。


不仅仅是shell脚本,任何文本形式的文件(配置文件,脚本文件,其他log文件等等),
都可以考虑用以上这种方法附带。。。


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