前言
因为Android系统的内核是Linux,所以可以在Android系统中执行shell脚本。我们常用的Android调试工具adb中有很多指令前面都是adb shell开头,这就是adb把这些指令当作shell指令的方式在Android系统中执行。所以我们完全可以把这些adb指令编写成一个shell脚本,然后在Android系统中执行这个脚本,就可以让Android系统自己把shell脚本中的指令全部执行。如果脚本中有循环语句,就会反复执行循环中的指令,以达到某些压力测试的目的。
常用的adb指令
使用adb的时候需要注意:当前电脑上只连接了一台打开USB调试功能的手机时,可以直接使用以下adb指令;当前电脑上连接了多台打开USB调试功能的手机时,需要在adb后面加-s参数来指定操作某台设备(adb devices 除外)。
adb ...
adb -s 设备序列号 ...
查看设备序列号
指令:adb devices
重启设备
指令: adb reboot
安装卸载APP
安装APP
指令:adb install app路径(存放在电脑中的那个位置)
卸载APP
指令:adb uninstall 要卸载的APP包名
获取APP包名
指令:adb shell pm list package -3
解释:adb shell为指令开头,表示执行shell指令;pm是package manager的缩写,此指令可以执行package相关的操作;list package可以查看手机上所有的package;-3表示第三方安装的APP。我们还可以用-i表示系统APP,如果不用参数则输出所有package。
推送和获取文件
推送文件
可以把电脑上的文件推送到手机上
指令:adb push 电脑上的文件路径 手机路径
获取文件
可以把手机上的文件推送到电脑上
指令:adb pull 手机上的文件路径 电脑路径
点击(tap)
指令:adb shell input tap x y
解释:adb shell为指令开头,表示执行shell指令;input为输入某个事件;tap为点击事件;x和y表示横纵坐标值,我们想点击屏幕中的某个点,就把横纵坐标值替换掉指令中的x和y。整个指令的意思为:输入一个点击事件,点击坐标为(x, y)。
坐标获取方法:在系统设置中找到手机系统的版本号,连续点击7次版本号打开开发者模式,在开发者模式中勾选指针位置选项。这时我们点击手机屏幕,就能看到点击位置的坐标了。
滑动(swipe)
指令:adb shell input swipe x1 y1 x2 y2 time
解释:adb shell为指令开头,表示执行shell指令;input为输入某个事件;swipe为滑动事件;x1和y1为起点横纵坐标,x2和y2为终点横纵坐标,time为整个滑动过程需要的时间(控制滑动的快慢,单位ms)。整个指令的意思为:输入一个滑动事件,从(x1, y1)滑动到(x2, y2),整个滑动过程0毫秒时在(x1, y1)time毫秒时抵达(x2, y2)。
长按屏幕上的某个点
通过巧用滑动事件可以达到长按的功能,当滑动的起点和终点为同一个点(x, y)时,就可以实现一直按住这个点(x, y)time毫秒。
按键(keyevent)
指令:adb shell input keyevent key
解释:adb shell为指令开头,表示执行shell指令;input为输入某个事件;keyevent为按键事件;key为键值。整个指令的意思为:输入一个按键事件,触发键值为key的键对应的事件。
adb常用键值表
中文名称 | 键值 | 中文名称 | 键值 | 中文名称 | 键值 | 中文名称 | 键值 |
按键Home | 3 | 导航键 确定 | 23 | 按键'N' | 42 | 通知键 | 83 |
返回键 | 4 | 音量增加键 | 24 | 按键'O' | 43 | 搜索键 | 84 |
拨号键 | 5 | 音量减小键 | 25 | 按键'P' | 44 | 话筒静音键 | 91 |
挂机键 | 6 | 电源键 | 26 | 按键'Q' | 45 | 向上翻页键 | 92 |
按键'0' | 7 | 拍照键 | 27 | 按键'R' | 46 | 向下翻页键 | 93 |
按键'1' | 8 | 按键'A' | 29 | 按键'S' | 47 | ESC键 | 111 |
按键'2' | 9 | 按键'B' | 30 | 按键'T' | 48 | 删除键 | 112 |
按键'3' | 10 | 按键'C' | 31 | 按键'U' | 49 | 大写锁定键 | 115 |
按键'4' | 11 | 按键'D' | 32 | 按键'V' | 50 | 滚动锁定键 | 116 |
按键'5' | 12 | 按键'E' | 33 | 按键'W' | 51 | Break/Pause键 | 121 |
按键'6' | 13 | 按键'F' | 34 | 按键'X' | 52 | 光标移动到开始 | 122 |
按键'7' | 14 | 按键'G' | 35 | 按键'Y' | 53 | 光标移动到末尾 | 123 |
按键'8' | 15 | 按键'H' | 36 | 按键'Z' | 54 | 插入键 | 124 |
按键'9' | 16 | 按键'I' | 37 | Tab键 | 61 | 小键盘锁 | 143 |
导航键 向上 | 19 | 按键'J' | 38 | 回车键 | 66 | 扬声器静音键 | 164 |
导航键 向下 | 20 | 按键'K' | 39 | 退格键 | 67 | 放大键 | 168 |
导航键 向左 | 21 | 按键'L' | 40 | 拍照对焦键 | 80 | 缩小键 | 169 |
导航键 向右 | 22 | 按键'M' | 41 | 菜单键 | 82 | 任务键 | 187 |
获取自己开发的特殊按键
当我们遇到某些Android设备具有自己开发的特殊按键时,怎样去获取这个特殊按键的键值。如果认识开发者,可以询问开发者;如果不认识开发者,可以尝试如下指令来获取。
指令:adb shell "logcat |grep "keycode""
解释:adb shell为指令开头,表示执行shell指令;logcat为获取系统日志;|为筛选管道符号;grep "keycode"为查找出包含字符串"keycode"的行。
用法:在电脑的终端输入上面的指令后,按一下Android设备上的特殊按键(注意不要碰到触摸屏和其他按键),即可得到键值。如果没有输出信息,那只能问开发者了。
模拟按键(sendevent)
模拟按键只对Android10以下的系统有效,Android10以后Google出于安全考虑,禁止使用sendevent模拟按键。这里的模拟按键是真正的模拟出类似用手去按键的效果,上面的按键只是发送按键会触发的事件。例如上面的按键可以按电源键来亮灭屏,但不能长按电源键来弹出关机按钮,这里的模拟按键就可以。
获取按键事件
指令:adb shell getevent -c 4
解释:adb shell为指令开头,表示执行shell指令;getevent为获取按键事件;-c 4为获取前面4行信息后立即返回。
用法:在电脑的终端输入上面的指令后,按一下Android设备上的要模拟的按键(注意不要碰到触摸屏和其他按键),即可得到按键事件。下面的4行信息是按音量+键得到的。
/dev/input/event1: 0001 0073 00000001
/dev/input/event1: 0000 0000 00000000
/dev/input/event1: 0001 0073 00000000
/dev/input/event1: 0000 0000 00000000
上面的4行信息中,前面两行表示按下音量+键;后面两行表示抬起音量+键。其中0073就表示音量+的按键值,这和adb的键值不是一个东西要注意区分。0073是一个16进制数,要转换为10进制数使用,转换为10进制数为0115。
发送按键事件(模拟按键)
模拟长按音量+键的指令如下(在adb shell中执行):
sendevent /dev/input/event1 0001 0115 00000001
sendevent /dev/input/event1 0000 0000 00000000
sleep 1
sendevent /dev/input/event1 0001 0115 00000000
sendevent /dev/input/event1 0000 0000 00000000
上面的5行指令中,前面两行表示按下音量+键;后面两行表示抬起音量+键,中间的sleep 1表示按下音量键后停止1秒,就是按住不动保持1秒后再抬起音量+键。这样就达到了长按音量+键的作用。
打开APP
每个APP都会有一个主activity,我们只需要去启动这个主activity,就可以打开这个APP。首先我们需要去获取这个APP的主activity,再通过adb指令来启动主activity,从而实现打开APP。
获取activity
指令:adb shell "dumpsys window | grep mCurrentFocus"
解释:adb shell为指令开头,表示执行shell指令;dumpsys为查询系统服务运行状态;window为获取手机当前界面信息;| 为筛选管道符号;grep为Linux的筛选指令,| grep mCurrentFocus为筛选出含有字符串mCurrentFocus的行。
用法:我们先在手机上打开某个APP,再执行上面adb的指令,我们就能得到这个APP当前activity的信息。例如:我们在手机上打开谷歌浏览器,再执行上面的adb指令,可以得到如下信息。
mCurrentFocus=Window{b07d5d u0 com.android.chrome/com.google.android.apps.chrome.Main}-[Surface(name=*Title#569)/@0x2b077cd]
其中的com.android.chrome/com.google.android.apps.chrome.Main就是我们需要的activity。
启动activity
指令:adb shell am start -S -n activity
解释:adb shell为指令开头,表示执行shell指令;am为activity manager的缩写,此指令可以模拟系统行为;start为启动activity指令;-S为关闭activity所属的APP进程后再启动activity(俗称冷启动);-n为指定要启动的activity名称。不加-S也可以,不加-S还能发现APP是否存在内存泄漏。
用法:我们获取到某个APP的主activity后,使用上面的指令直接启动这个activity。例如:我们可以使用adb shell am start -n com.android.chrome/com.google.android.apps.chrome.Main启动谷歌浏览器。
重复冷启动APP
指令:adb shell am start -S -R times -W activity
解释:adb shell为指令开头,表示执行shell指令;am为activity manager的缩写,此指令可以模拟系统行为;start为启动activity指令;-S为关闭activity所属的APP进程后再启动activity(俗称冷启动);-R为重复启动指令,times为重复次数,要重复启动APP几次就把times替换成数字几;-W为等待APP启动完成指令,会在APP启动完成后输出启动时间。
重复热启动APP
指令:adb shell am start -R times -W activity
解释:冷启动不加-S参数就是热启动
拨打电话
指令:adb shell am start -a android.intent.action.CALL -d tel:10086
解释:adb shell为指令开头,表示执行shell指令;am为activity manager的缩写,此指令可以模拟系统行为;start为启动activity指令;-a为发送广播指令;android.intent.action.CALL为拨打电话广播;-d为指定参数指令;tel:10086为拨打的电话号码为10086。
打开网页
指令:adb shell am start -a android.intent.action.VIEW -d http://www.baidu.com
解释:adb shell为指令开头,表示执行shell指令;am为activity manager的缩写,此指令可以模拟系统行为;start为启动activity指令;-a为发送广播指令;android.intent.action.VIEW为根据数据类型打开Android默认应用广播;-d为指定数据指令;http://www.baidu.com为打开页面的url地址。
播放视频
指令:adb shell am start -a "android.intent.action.VIEW" -t "video/*" -d "file:///storage/sdcard0/Movies/冰川时代.mp4"
解释:adb shell为指令开头,表示执行shell指令;am为activity manager的缩写,此指令可以模拟系统行为;start为启动activity指令;-a为发送广播指令;android.intent.action.VIEW为根据数据类型打开Android默认应用广播;-t为指定应用类型;video/*为应用类型;-d为指定参数指令;file:///storage/sdcard0/Movies/冰川时代.mp4为视频冰川时代.mp4的文件路径。还可以用-n来指定具体的应用,就不需要-t来指定应用类型了。
播放音乐
指令:adb shell am start -a "android.intent.action.VIEW" -t "audio/mp3" -d "file:///storage/sdcard0/Music/nomy.mp3"
解释:adb shell为指令开头,表示执行shell指令;am为activity manager的缩写,此指令可以模拟系统行为;start为启动activity指令;-a为发送广播指令;android.intent.action.VIEW为根据数据类型打开Android默认应用广播;-t为指定应用类型;audio/mp3为应用类型;-d为指定参数指令;file:///storage/sdcard0/Music/nomy.mp3为音乐nomy.mp3的文件路径。还可以用-n来指定具体的应用,就不需要-t来指定应用类型了。
获取屏幕元素信息
指令:adb shell uiautomator dump
解释:adb shell为指令开头,表示执行shell指令;uiautomator为系统操作UI元素模块;dump为输出UI界面信息指令。
用法:执行完上面的指令后,会得到一个xml的文件(/sdcard/window_dump.xml)。我们用adb pull把它推送到电脑上,就可以使用python对它进行分析(xml.etree.ElementTree)。
屏幕常亮设置
开启保持常亮
指令:adb shell svc power stayon true
解释:adb shell为指令开头,表示执行shell指令;svc为系统服务相关指令;power stayon为屏幕常亮相关操作指令;true为开启指令。
关闭保持常亮
指令:adb shell svc power stayon false
解释:adb shell为指令开头,表示执行shell指令;svc为系统服务相关指令;power stayon为屏幕常亮相关操作指令;false为关闭指令。
插入USB时常亮
指令:adb shell svc power stayon usb
解释:adb shell为指令开头,表示执行shell指令;svc为系统服务相关指令;power stayon为屏幕常亮相关操作指令;usb为插入USB时保持常亮指令。
插入电源时常亮
指令:adb shell svc power stayon ac
解释:adb shell为指令开头,表示执行shell指令;svc为系统服务相关指令;power stayon为屏幕常亮相关操作指令;ac为插入电源时保持常亮指令。
开关WiFi
打开WiFi
指令:adb shell svc wifi enable
解释:adb shell为指令开头,表示执行shell指令;svc为系统服务相关指令;wifi为Wifi相关操作指令;enable为启动指令。
关闭WiFi
指令:adb shell svc wifi disable
解释:adb shell为指令开头,表示执行shell指令;svc为系统服务相关指令;wifi为Wifi相关操作指令;disable为关闭指令。
开关数据流量
打开数据流量
指令:adb shell svc data enable
解释:adb shell为指令开头,表示执行shell指令;svc为系统服务相关指令;data为Data相关操作指令;enable为启动指令。
关闭数据流量
指令:adb shell svc data disable
解释:adb shell为指令开头,表示执行shell指令;svc为系统服务相关指令;data为Data相关操作指令;disable为关闭指令。
开关蓝牙
打开蓝牙
指令:adb shell svc bluetooth enable
解释:adb shell为指令开头,表示执行shell指令;svc为系统服务相关指令;bluetooth为Bluetooth相关操作指令;enable为启动指令。
关闭蓝牙
指令:adb shell svc bluetooth disable
解释:adb shell为指令开头,表示执行shell指令;svc为系统服务相关指令;bluetooth为Bluetooth相关操作指令;disable为关闭指令。
开关NFC
打开NFC
指令:adb shell svc nfc enable
解释:adb shell为指令开头,表示执行shell指令;svc为系统服务相关指令;nfc为NFC相关操作指令;enable为启动指令。
关闭NFC
指令:adb shell svc nfc disable
解释:adb shell为指令开头,表示执行shell指令;svc为系统服务相关指令;nfc为NFC相关操作指令;disable为关闭指令。
截屏和录屏
截屏
手机截屏放到手机存储内存中
指令:adb shell screencap -p /sdcard/xxx.png
解释:adb shell为指令开头,表示执行shell指令;screencap为截屏指令;-p为指定存放位置;/sdcard为手机存储内存根目录;xxx.png为名为xxx的png图片。
录屏
手机录屏放到手机存储内存中
指令:adb shell screenrecord /sdcard/xxx.mp4
解释:adb shell为指令开头,表示执行shell指令;screenrecord为录屏指令;/sdcard为手机存储内存根目录;xxx.mp4为名为xxx的mp4视频。
获取当前运行内存使用情况
手机总运行内存使用情况
指令:adb shell "dumpsys meminfo | grep Used"
解释:adb shell为指令开头,表示执行shell指令;dumpsys为查询系统服务运行状态;meminfo为查询内存的所有信息;| 为筛选管道符号;grep为linux筛选指令;Used为指定筛选的字符串。| grep Used总体为筛选出包含字符串Used的行。
单个APP运行内存使用情况
指令:adb shell "dumpsys APP包名 | grep PSS | awk '{print $3}'"
解释:adb shell为指令开头,表示执行shell指令;dumpsys为查询系统服务运行状态;meminfo为查询内存的所有信息;| 为筛选管道符号;grep为linux筛选指令;PSS为指定筛选的字符串;awk为linux列筛选指令;'{print $3}'为打印出第3列的数据;| grep PSS | awk '{print $3}'总体为筛选出包含字符串PSS的行中第3列的数据,并打印出来。
获取手机屏幕大小
指令:adb shell wm size
解释:adb shell为指令开头,表示执行shell指令;wm是window manager的缩写,为屏幕管理相关的指令;size为返回屏幕大小的指令。
获取手机屏幕亮灭状态
指令:adb shell "dumpsys display | grep mScreenState"
解释:adb shell为指令开头,表示执行shell指令;dumpsys为查询系统服务运行状态;display为屏幕显示相关信息;| 为筛选管道符号;grep为linux筛选指令;mScreenState为指定筛选的字符串;| grep mScreenState总体为筛选出包含字符串mScreenState的行。
亮灭屏
亮屏
指令:adb shell input keyevent 224
解释:adb shell为指令开头,表示执行shell指令;input为输入某个事件;keyevent为按键事件;224为键值。总体意思为输入亮屏事件。
灭屏
指令:adb shell input keyevent 223
解释:adb shell为指令开头,表示执行shell指令;input为输入某个事件;keyevent为按键事件;223为键值。总体意思为输入灭屏事件。
查看电池信息
指令:adb shell dumpsys battery
解释:adb shell为指令开头,表示执行shell指令;dumpsys为查询系统服务运行状态;battery为电池信息。执行此命令后我们可以得到充电状态、电池电量、电池温度、电池电压等信息。
恢复出厂设置
Google在Android10以上的版本中提供了用于恢复出厂设置压力测试的指令,此指令会禁止各厂商对Android系统的部分自定义功能。因此使用此指令恢复出厂设置后,会没有开机向导,没有锁屏,USB调试功能为开启状态。所以我们可以连续使用此指令来对手机进行恢复出厂设置的压力测试。
指令:adb shell cmd testharness enable
shell脚本
要编写shell脚本,我们需要先了解一些基本的shell语法。这里只简单介绍shell脚本的语法,通过这些简单的介绍我们足以写出手机的自动化测试脚本。如果你想了解更多shell脚本内容,可以在优快云中搜索shell脚本,你一定可以找到更详细的介绍。
变量
普通变量
直接给变量赋值,千万不要在=号两边留空格(我python写多了,老是习惯性的留空格,结果发现报错了,找了半天才找到原因)。
name='小明'
age=18
命令变量
执行完shell指令后再把返回的结果赋值给变量,命令变量有如下两种写法。
# 得到手机屏幕亮灭状态
screenState=`dumpsys display | grep mScreenState`
# 得到手机屏幕大小
winSize=$(wm size)
得到变量的值
查看变量的值有4中写法:$变量、"$变量"、${变量}、"${变量}"。其中${变量}和"${变量}"这两种有特殊用法,可以截取出变量的部分值:${变量:起始位置}、${变量:起始位置:截取长度}。
screenState=$(dumpsys display | grep mScreenState)
echo screenState # screenState
echo $screenState # mScreenState=OFF
echo "$screenState" # mScreenState=OFF
echo ${screenState} # mScreenState=OFF
echo ${screenState:15:3} # OFF
echo "${screenState}" # mScreenState=OFF
echo "${screenState:15}" # OFF
echo指令为linux中的打印输出指令,此指令使用重定向符号>(覆盖)和>>(追加)可以把输出内容写入一个文本中。
测试语句
测试语句的作用是判断一个条件表达式是否成立,如果成立返回0,不成立返回1。测试语句有如下两种写法:
写法一
[ 1 = 2 ] # 1
写法二
test 1 = 1 # 0
返回的0有可能没显示在命令终端,但不影响它确实返回了0。
数值大小比较
-eq | 判断左右两边是否相等 |
-ne | 判断左右两边是否不相等 |
-gt | 判断左边是否大于右边 |
-lt | 判断左边是否小于右边 |
-ge | 判断左边是否大于等于右边 |
-le | 判断左边是否小于等于右边 |
我们使用上面的测试语句来做演示。
[ 1 -eq 1 ] # 0
[ 1 -ne 2 ] # 0
[ 2 -gt 1 ] # 0
[ 1 -lt 2 ] # 0
[ 1 -ge 1 ] # 0
[ 2 -ge 1 ] # 0
[ 1 -le 1 ] # 0
[ 1 -le 2 ] # 0
数值计算(let)
let指令可以执行整数的四则运算。
number=1
let number2=number+1
echo $number2 # 2
let number3=number2*3
echo $number3 # 6
let number4=number3/2
echo $number4 # 3
let number5=number4-1
echo $number5 # 2
let还支持+=、-=、*=、/=赋值运算符。
number=1
let number+=1
echo $number # 2
let number-=1
echo $number # 1
let number*=3
echo $number # 3
let number/=3
echo $number # 1
字符串
判断是否相等
== | 判断左右两边的字符串是否相等 |
!= | 判断左右两边的字符串是否不相等 |
这里同样使用测试语句来给大家做演示,现在我的手机处于灭屏状态。
screenState=$(dumpsys display | grep mScreenState)
echo ${screenState:15:3} # OFF
[ ${screenState:15:3} == 'OFF' ] # 0
screenState=$(dumpsys display | grep mScreenState)
echo ${screenState:15:3} # OFF
[ ${screenState:15:3} != 'OFF' ] # 1
输出字符串的长度
${#字符串}可以输出字符串的长度。
string='abcde'
echo ${#string} # 5
数组的应用
bash中支持数组,数组中元素之间的分隔符采用的是空格,且只支持一维数组,不支持多维数组。数组支持索引,第一个元素索引下标为0,后面的元素索引下标依次递增。
keylist=(3 4 5 6 7)
echo "home键是:${keylist[0]}" # home键是:3
echo "back键是:${keylist[1]}" # back键是:4
一次性查看所有元素可以通过*和@来实现。
keylist=('hello' 'world' 1 2 3)
echo "所有元素:${keylist[*]}" # 所有元素:hello world 1 2 3
echo "所有元素:${keylist[@]}" # 所有元素:hello world 1 2 3
# 结合#号可以得到数组的长度(元素个数)
keylist=('hello' 'world' 1 2 3)
echo "数组长度为:${#keylist[*]}" # 数组长度为:5
echo "数组长度为:${#keylist[@]}" # 数组长度为:5
我们可以通过索引下标修改数组中对应元素的值。
keylist=(3 4 5 6 7)
keylist[3]=187
echo "任务键是:${keylist[3]}" # 任务键是:187
当索引下标对应的元素不存在时为新增一个元素。
keylist=('hello' 'world' 1 2 3)
echo "所有元素:${keylist[*]}" # 所有元素:hello world 1 2 3
keylist[5]='haha'
echo "所有元素:${keylist[@]}" # 所有元素:hello world 1 2 3 haha
keylist[6]='xixi'
echo "所有元素:${keylist[*]}" # 所有元素:hello world 1 2 3 haha xixi
我们可以通过unset指令删除数组中的某个元素或删除整个数组。
keylist=('hello' 'world' 1 2 3)
echo "所有元素:${keylist[*]}" # 所有元素:hello world 1 2 3
unset keylist[0]
echo "所有元素:${keylist[@]}" # 所有元素:world 1 2 3
unset keylist
echo "我无了:${keylist[*]}" # 我无了:
if判断语句
基本结构
if [ 条件 ]
then
满足条件时要执行的语句(缩进一个Tab键或4个空格)
else
不满足条件时要执行的语句
fi
if后面是一个测试语句,首先判断if后面的测试语句返回的值是否为0。为0则执行then下面的语句,为1则执行else下面的语句。举个例子:
value='hello'
if [ 1 -gt 2 ]
then
value='1 大于 2'
else
value='1 不大于 2'
fi
echo "判断结果:$value" # 判断结果:1 不大于 2
当我们不需要执行else下面的语句时,可以省略else语句。
value='hello'
if [ 1 -gt 2 ]
then
value='1 大于 2'
fi
echo "判断结果:$value" # 判断结果:hello
多条件结构
if [ 条件1 ]
then
满足条件1时要执行的语句(缩进一个Tab键或4个空格)
elif [ 条件2 ]
then
满足条件2时要执行的语句
elif [ 条件3 ]
then
满足条件3时要执行的语句
.
.
.
elif [ 条件n ]
then
满足条件n时要执行的语句
else
不满足所有条件时要执行的语句
fi
多条件结构比基本结构多了elif语句,elif可以无限的增加,直到把我们所有的判断条件都添加进去。首先判断条件1,条件1成立时执行满足条件1时要执行的语句后结束if语句;条件1不成立时判断条件2,条件2成立时执行满足条件2时要执行的语句后结束if语句;条件2不成立时判断条件3,以此类推;当所有条件都不成立时执行else下面的语句。举个例子,我们来给小学生的数学成绩评等级,分数大于等于90的为优秀,大于等于80分的为良好,大于60分的为普通,等于60分的为及格,小于60分的为不及格。
score=85
level='未评级'
if [ score -ge 90 ]
then
level='优秀'
elif [ score -ge 80 ]
then
level='良好'
elif [ score -gt 60 ]
then
level='普通'
elif [ score -eq 60 ]
then
level='及格'
else
level='不及格'
fi
echo "成绩 $score 对应的评级为:$level" # 成绩 85 对应的评级为:良好
暂停语句
sleep指令,sleep指令可以让上下两行语句之间存在执行间隔。
input keyevent 223
sleep 1
input keyevent 224
先执行input keyevent 223让手机屏幕灭屏,暂停1秒钟后再执行input keyevent 224让手机屏幕亮屏。
循环语句
for循环语句
for循环语句有两种使用方式,第一种类似python中的for循环语句。第二种类似Java、JavaScript、C++中的for循环(这种方式不适合Android系统直接忽略)。
for i in var1 var2 var3 . . . . varn
do
每次循环要执行的语句
done
第一次循环把var1的值赋给变量i,第二次循环把var2的值给变量i,以此类推直到把varn的值赋给变量i后,结束循环。举个例子:
number_list=(1 2 3 4 5)
number_sum=0
for i in ${number_list[*]} # for i in 1 2 3 4 5
do
let number_sum+=i
done
echo $number_sum # 15
while循环语句
while循环语句通过判断条件的真假来进行循环,条件为真时执行循环语句,条件为假时结束循环。
while ((条件))
do
每次循环要执行的语句
done
注意(())中的条件比较符号使用==、!=、<、>、<=、>=,与测试语句要区分开。举个例子:
number=0
while ((number<10))
do
let number+=1
done
echo $number # 10
无限循环可以使用永远为真的条件如:((1==1))、((1)),或者直接使用true。
while true
do
echo "hello world"
sleep 1
done
以上代码会一直打印hello world,每间隔1秒打印一次。
continue
当执行到continue语句时会结束本次循环,continue之后的代码不会被执行。continue在for循环和while循环中使用方式一样,使用效果一样。
number=0
number_sum=0
while ((number<10))
do
let number+=1
if [ number -gt 5 ]
then
continue
fi
let number_sum+=number
done
echo $number_sum # 15
当number > 5时就会执行到continue,continue被执行就会结束本次循环进入到下一次循环,所以let number_sum+=number就不会被执行,所以循环结束后number_sum的值为1+2+3+4+5=15。
break
当执行到break语句时会直接结束循环,不再进行下一次循环。break在for循环和while循环中使用方式一样,使用效果一样。
number_sum=0
for i in 1 2 3 4 5 6 7 8 9 10
do
let number_sum+=i
if [ i -gt 5 ]
then
break
fi
done
echo $number_sum # 21
当i > 5时就会执行break,break被执行就会直接结束循环,此时number_sum的值为1+2+3+4+5+6=21。
定义函数
bash也支持函数封装的写法,我们可以把一些频繁使用的过程语句封装为一个函数。在shell脚本中定义函数的位置要在使用函数的位置之前,不然会存在函数未定义的错误。
基本结构
# 无参函数
函数名(){
函数内的语句
}
# 无参函数使用方式
函数名
# 有参函数
函数名(){
函数内的语句(内有参数$1、$2的使用)
}
# 有参函数使用方式
函数名 值1 值2
无参函数
不需要传入参数的函数,直接用函数名来调用。
my_print(){
echo "hello world"
}
my_print # hello world
有参函数
需要传入参数的函数,函数内部用$1、$2...$n来表示第一个第二个到第n个参数。调用方式是函数名加要传入的值(使用空格间隔开)。
my_info(){
echo "我叫:$1 今年:$2 岁"
}
my_info '小明' 18 # 我叫:小明 今年:18 岁
RANDOM随机数
RANDOM是bash中用来生成随机数的内置函数,使用RANDOM可以生成一个0~32767中的随机数。
echo $RANDOM # 19568
echo $RANDOM # 27608
echo $RANDOM # 569
可以使用$((RANDOM%number))和$((RANDOM%number+number))的方式来指定随机数的范围。
生成0~9范围内的随机数写法如下:
echo $((RANDOM%10)) # 3
RANDOM的起始值是0,终止值可以自定义,但RANDOM取不到终止值。
生成10~20范围内的随机数写法如下:
echo $((RANDOM%11+10)) # 15
$(()RANDOM%11)本来生的成随机数是0~10中的一个数,但我们在这个数后面+10,就变成了10~20中的数了。
你有没有想过,我们使用RANDOM+数组+循环+按键指令,可以让手机不断的随机按键。
keylist=(3 4 187) # 定义要随机按键的键值数组
while true
do
key=${keylist[$((RANDOM%3))]} # 用0~2中的随机下标取出一个键值
input keyevent key # 发送按键事件
sleep 2
done
shell自动化测试脚本
有了上面的基础知识后,我们就可以进行编写自动化测试脚本了。adb指令中,所有adb shell开头的指令都可以放到shell脚本中,前提是把adb shell去掉,只要adb shell后面的部分。
编写自动化测试脚本
现在我们来写一个反复亮灭屏的shell脚本。
while true
do
input keyevent 223
sleep 2
input keyevent 224
sleep 2
done
写一个持续进入退出谷歌浏览器的shell脚本,在打开谷歌浏览器之后判断一下是否有闪退的情况,并把测试日志写入到手机的存储内存当中。
activity='com.android.chrome/com.google.android.apps.chrome.Main'
count=1
while true
do
am start -n $activity
sleep 3
info=$(dumpsys window | grep mCurrentFocus | awk '{print $3}')
package=${info:0:${#activity}}
if [ $package == $activity ]
then
echo "第 $count 次打开谷歌浏览器,打开成功" >> /sdcard/ChromeTest.txt
else
echo "第 $count 次打开谷歌浏览器,打开失败---------" >> /sdcard/ChromeTest.txt
fi
input keyevent 4
sleep 2
let count+=1
done
我已经给出了两个例子,你可以根据你的测试需求,结合上面的知识点编写出自动化测试脚本。如果你会python或C++,你可以尝试制作一个快速编写脚本的工具。python可以使用tkinter框架或者pyside6框架,轻量化的工具使用tkinter最好,C++可以使用QT框架。
执行自动化测试脚本
我以当前文件夹下的测试脚本ChromeTest.sh为列,为大家演示脚本的执行过程。
推送测试脚本到手机
指令:adb push ChromeTest.sh /data/local/tmp
解释:用adb命令把我们编写的shell脚本推送到手机/data/local/tmp目录下。
修改脚本格式
如果我们的脚本是在Windows上编写的,要把脚本文件的格式转换为Unix格式。如果是在Linux上编写的请忽略此命令。
指令:adb shell "dos2unix /data/local/tmp/ChromeTest.sh"
解释:使用dos2unix指令把ChromeTest.sh文件的格式转换为Unix格式。
授予执行权限
指令:adb shell "chmod 777 ChromeTest.sh"
解释:使用chmod指令给文件ChromeTest.sh授予读、写、执行的权限。
执行脚本
指令:adb shell "nohup ./data/local/tmp/ChromeTest.sh > /dev/null &"
解释:使用nohup指令使我们的脚本在后台运行,> /dev/null表示脚本的输出重定向到/dev/null文件夹,&表示脱机运行。使用了&我们拔掉USB线之后shell脚本会继续在手机上运行。
结束自动化测试脚本
如果我们想提前结束正在手机上运行的shell脚本,有两种方式可以完成。第一种直接关机或重启,第二种使用kill指令结束shell脚本。
获取shell脚本进程号
指令:adb shell "ps | grep sh$ | awk '{print $2}'"
解释:使用ps指令查看手机中的所有进程信息;使用grep筛选出以sh结尾的行;使用awk打印出第二列的数据。
通过上面的指令我们至少可以得到两个进程号。有一个是系统的shell进程,它的进程号随时在改变我们没有办法结束它;有一个是shell脚本的进程,我们可以使用kill指令结束它。所以我们可以对这两个进程号都使用kill指令,反正系统的shll进程我们是没办法结束的。
结束shell脚本进程
指令:adb shell "kill PID"
解释:使用kill指令强行结束进程;PID为要结束的进程号。
建议
如果你会python或C++,我建议你写一个带有UI界面的工具来执行shell脚本和结束shell脚本,把上面的指令都封装进去。通过UI界面来选择shell脚本文件,点击按钮一键执行,可比一个一个指令去执行方便多了。