1.前言
autojs的shell命令类似于adb执行“adb shell”命令,就是将adb命令中的前缀“adb shell”去掉,后缀部分通过autojs给出的方式执行。当然,shell命令执行需要在root环境下才能生效。autojs对adb shell命令大多数情况是支持的,但涉及内存信息获取或者操作部分的shell命令是无法执行的,这种命令既不报错也不会返回信息。
2.函数
1.概况
autojs给出了两种执行shell命令的方式,第一种是直接调用shell函数,另一种是构建一个Shell对象再调用对象的函数。我们使用第一种方式时,一般会以root权限的方式运行,这种方式每次都会自动创建个shell对象,运行方式采用同步运行,执行速度相对较慢,但是权限较高;我们使用第二种方式时,会先手动创建个包含root权限的shell对象,然后调用这个对象的函数来执行shelll命令,我们在整个代码可以创建一个shell对象,运行方式采用异步运行,这种方式执行速度相对较快,但是权限较低。
一般情况下,我们可以在代码开始构建一个shell对象,然后通过这个对象来执行命令,当shell对象由于权限不够而无法使命令生效时,再通过直接调用shell函数的方式执行。但是,下面的使用场景中也介绍了两种方式的使用情况,可以按照这个使用场景进行使用。
2.shell函数
用于执行shell命令,需要传递两个参数,分别为命令和是否以root权限运行(默认为false),参数类型分别为字符串和boolean,返回执行结果,返回类型为对象。执行结果对象属性有code、result和error,分别表示返回码、运行结果和运行错误信息。其中,code为0时表示运行成功,不为0时代表运行失败。
以“adb shell pm list packages”命令为例,怎么在autojs中执行这个命令。我前面介绍了很多关于root权限才能使用的函数,我的autojs早已经获取了root权限,忽略了这个情况。我下面代码会把我认为比较好用获取root权限方式告诉大家。如果使用root权限的功能,获取root权限代码一般放在脚本开头。但是学习阶段防止代码干扰,之后不再介绍获取root权限的功能。
这是电脑cmd中调用adb shell命令查看雷电模拟器应用信息的情况,大家会发现,每次执行abd shell命令都会重启adb,因为我电脑上拥有adb的应用(Android Studio、按键精灵手机助手、雷电模拟器4、雷电模拟器9)太多了,出现这种情况主要是abd版本冲突导致的。幸好不影响功能,我这里就处理了,大家知道就好了。
在autojs可以通过以下方式获取root权限和执行shell命令。
// 获取root权限
if (!hasRootAccess()) {
toast("请开启Root权限");
// 如果请求root权限失败
if (!requestRoot()) {
// 清理缓存,退出脚本
// 由于这是学习阶段,没有缓存就直接退出了
exit();
}
}
let result = shell("pm list packages", true);
// 如果执行成功
if (result.code == 0) {
console.log("执行shell命令成功");
console.log(result.result);
} else {
console.log("执行shell命令失败");
console.log(result.error);
}
// 检查是否有Root权限
function hasRootAccess() {
let result = shell("id", true).result; // 执行Linux系统命令
return (result.indexOf("uid=0") >= 0); // uid=0表示Root用户
}
// 请求Root权限
function requestRoot() {
let sh = new Shell(true); // 创建交互式Shell
sh.exec("su"); // 发送Root权限请求
// 等待用户手动授权(弹出系统授权弹窗)
sleep(5000); // 留出5秒操作时间
if (hasRootAccess()) {
toast("Root权限已获取");
return true;
} else {
toast("Root权限请求失败");
return false;
}
}
3.Shell对象
Shell对象,构造函数允许传递一个是否以root权限运行(默认为false)一个参数,参数类型为boolean,我们一般会以root权限创建对象,返回shell对象。返回shell对象的函数有exec、exit、exitAndWaitFor和setCallback,分别用于执行shell命令、退出shell命令、等待命令运行结束和运行回调。
exec函数用于执行shell命令,需要传递命令一个参数,参数类型为字符串,这个函数无返回值,如果想获取返回数据需要通过setCallback回调函数。
exit函数用于退出命令运行,正在运行的命令也会终止,一般出现特殊情况下才会调用这个函数,正常结束的函数可以不调用这个函数。
exitAndWaitFor函数用于等待命令运行结束,通过exec函数运行方式是异步运行的,需要调用这个函数保证命令执行完成,避免后面代码提前运行而导致出错。
setCallback函数用于获取运行过程中的数据,包含onOutput和onNewLine两个函数,onOutput函数每次返回数据加载完成都会调用一次,onNewLine函数每次返回数据加载一行都会调用一次。实际上,通过回调的方式获取返回数据非常不友好,会包含大量干扰信息,最终导致onOutput函数会获取各种空信息和结束信息以及onNewLine函数获取多行数据才调用一次和获取onOutput函数类似的无用信息。
let currentShell = new Shell(true);
// 执行shell命令
currentShell.exec("pm list packages");
// 保存通过新行拼接的字符串
let lineString;
// 保存通过新内容拼接的字符串
let contentString;
// 执行回调函数
currentShell.setCallback({
onNewLine: function (line) {
console.log("新行:" + line);
},
onOutput: function (content) {
console.log("新内容:" + content);
}
})
// 等待命令运行结束
currentShell.exitAndWaitFor();
// 退出命令行
currentShell.exit();
4.使用场景
通过上面的比较可以发现,Shell对象执行命令的方式局限性还是很大的,但是这种能够共用一个对象,执行速度快也是一个优点。我给出几个常用的场景:如果想快速执行shell命令,并且能够保证命令成功执行,可以使用Shell对象的方式;如果需要获取返回信息,无法保证执行命令的正确性或者需要很高的执行权限,可以使用shell函数的方式。
3.命令
官方文档介绍了很多命令,我就不一一介绍了。这些命令并不是包含所有adb shell命令,而且找起来很不方便。一般我需要哪个功能的adb shell就会通过AI搜索一下,然后通过autojs使用。这些命令不如函数一样方便查找,可以通过用到啥搜索啥的方式,然后直接使用和调试。我目前没有遇到命令不能执行的情况,除了查询和操作内存信息的时候。我们一般情况下,很少使用adb shell的方式完成某个功能,当我们有特殊需求时可以使用。
4.总结
特别注意,只有通过个人主页博客或者个人介绍中方式,才能获取源码