简单易懂!Android开机启动shell脚本新手入门教程
你是不是也遇到过这样的问题:想让Android设备一开机就自动执行某个任务,比如备份日志、初始化硬件、启动监控服务,或者调试时快速设置系统属性?但一看到“init.rc”“SELinux”“te文件”这些词就头大?别担心——这篇教程就是为你写的。
它不讲晦涩的底层原理,不堆砌术语,不假设你已经熟悉AOSP编译或SELinux策略编写。我们只聚焦一件事:用最直接的方式,让你在真实设备上跑通一个开机就能执行的shell脚本。哪怕你是第一次接触Android系统定制,只要会写几行shell、能用adb推文件、知道怎么重启手机,就能跟着做完。
全文没有“赋能”“生态”“深度集成”这类空话,只有可验证的步骤、可复制的代码、可复现的结果。每一步都标注了“为什么这么做”,哪里容易出错、怎么快速验证、报错了怎么看日志——全是工程师踩坑后总结的实操经验。
准备好了吗?我们从零开始。
1. 先搞清楚:你的目标到底是什么
在动手前,请确认你真正需要的是什么:
- 开机后自动运行一段命令(比如
setprop debug.test 1、logcat -f /data/log/boot.log) - 不需要图形界面,纯后台执行
- 脚本只需执行一次(oneshot)或常驻(daemon),不涉及复杂服务管理
- 你有root权限的测试机(或可刷自定义system镜像的开发板)
- ❌ 不是想做App级自启(那该用BroadcastReceiver)
- ❌ 不是想绕过厂商锁刷ROM(本教程不涉及解锁Bootloader)
- ❌ 不是想在用户空间App启动时触发(那是Framework层的事)
如果你的需求符合上面前三条,那就继续往下看。我们不追求“完美适配所有芯片平台”,而是先确保在主流Android 8.0+设备(如MTK/高通公版)上稳定跑通,再谈扩展。
2. 最小可行方案:三步走通开机脚本
很多教程一上来就让你改init.rc、写te策略、编译整个系统——这完全没必要。对于验证逻辑、快速调试、内部测试来说,我们可以用更轻量的方式实现目标。整个流程就三步:
- 写一个极简shell脚本,并手动验证它能在设备上运行
- 把脚本放进系统可执行路径,并赋予正确权限
- 在
init.rc中添加一条service声明,指定开机启动
后面两步(SELinux策略、te文件)我们放在“进阶排错”里讲——因为很多测试场景下,临时关闭SELinux或使用已有domain就能跳过它们。先让脚本能跑起来,比纠结策略文件更有价值。
2.1 写一个真正能用的shell脚本
新建一个文件,命名为init.test.sh,内容如下:
#!/system/bin/sh # 开机脚本标准开头:必须用/system/bin/sh,不是/bin/sh # Android的sh路径和Linux不同,写错直接静默失败 # 第一步:记录脚本已运行(方便验证是否真被调用) log -p i -t "boot_script" "init.test.sh started at $(date)" # 第二步:设置一个系统属性(最安全的测试动作,无副作用) setprop sys.boot.test 1 # 第三步:写入一行日志到/data(需确保/data可写) echo "Boot script executed on $(date)" > /data/boot_log.txt # 可选:如果需要持续运行(如监听事件),取消下面注释 # while true; do # log -p i -t "boot_script" "still alive..." # sleep 30 # done关键注意点:
- 第一行
#!/system/bin/sh必须严格匹配,不能写成#!/bin/sh或漏掉/system - 所有命令路径要写全(如
/system/bin/log而非log),避免PATH环境变量不可靠 - 避免创建新文件到
/system分区(只读),优先用/data或/dev等可写路径 log命令是Android专用日志工具,比echo更能被logcat捕获
验证方法(非常重要!):
用adb把脚本推到手机并手动执行一次:
adb push init.test.sh /data/local/tmp/ adb shell chmod +x /data/local/tmp/init.test.sh adb shell /data/local/tmp/init.test.sh adb logcat -t 10 -s boot_script如果看到"init.test.sh started at..."日志,且/data/boot_log.txt文件生成成功,说明脚本本身完全没问题。
2.2 把脚本放进系统路径并设好权限
Android开机时,init进程只信任特定路径下的可执行文件。最稳妥的位置是/system/bin/(需remount system为可写):
# 1. remount system分区为可写(需要root) adb root adb remount # 2. 推送脚本到/system/bin/ adb push init.test.sh /system/bin/init.test.sh # 3. 设置权限:所有者可读可写可执行,其他用户仅可执行 adb shell chmod 755 /system/bin/init.test.sh # 4. 验证权限是否正确 adb shell ls -l /system/bin/init.test.sh # 应输出:-rwxr-xr-x root root ... /system/bin/init.test.sh小技巧:如果你不想每次刷机都手动推,可以把这一步写成一个adb一键脚本,后续反复测试非常省时。
2.3 在init.rc中添加service声明
现在脚本已就位,下一步是告诉Android的init进程:“这个脚本,开机时请帮我跑一次”。
大多数设备不会让你直接修改/system/etc/init.rc(它是编译生成的),但几乎都支持加载额外的.rc文件。常见位置包括:
/system/etc/init/(Android 8.0+ 推荐方式,支持.rc文件自动加载)/system/etc/init/hw/init.<vendor>.rc(厂商自定义rc文件)/vendor/etc/init/(vendor分区,部分设备支持)
我们采用最通用的方式:在/system/etc/init/下新建一个test_boot.rc文件:
# /system/etc/init/test_boot.rc service test_boot /system/bin/init.test.sh class main user root group root oneshot seclabel u:object_r:shell_exec:s0各字段含义:
service test_boot:服务名,任意取,但不能和已有服务重名/system/bin/init.test.sh:脚本绝对路径class main:属于main类,会在zygote启动前运行user/group root:以root身份运行(必要,否则无法设置系统属性)oneshot:执行完即退出(适合初始化类脚本);若需常驻,改为disabled并在其他trigger中启用seclabel u:object_r:shell_exec:s0:复用系统已有的shell_execSELinux域(关键!避免立即写te文件)
推送并验证rc文件:
adb push test_boot.rc /system/etc/init/test_boot.rc adb shell chmod 644 /system/etc/init/test_boot.rc adb reboot重启后,等待约30秒,执行:
adb logcat -b events -t 20 | grep -i test adb logcat -t 20 -s boot_script adb shell cat /data/boot_log.txt如果三处都看到预期输出,恭喜你——开机启动脚本已成功运行。
3. 常见问题与快速排错指南
即使按上述步骤操作,也可能遇到“脚本没执行”“报Permission denied”“logcat看不到日志”等问题。别急,90%的问题都集中在以下四个环节,我们逐个击破:
3.1 脚本根本没被init调用?检查rc文件加载状态
init进程启动时会打印加载了哪些rc文件。用以下命令确认你的test_boot.rc是否被识别:
adb logcat -b events | grep -i "import\|parse" # 正常应看到类似:import /system/etc/init/test_boot.rc如果没看到,说明rc文件路径错误或格式非法。检查:
- 文件是否在
/system/etc/init/目录下(不是子目录) - 文件名必须以
.rc结尾 - rc文件内容不能有Windows换行符(
\r\n),用dos2unix转换 - 每行末尾不能有多余空格
3.2 脚本执行了但报错?看init日志定位
init进程的错误不会出现在普通logcat里,要用-b events缓冲区:
adb logcat -b events | grep -i "test_boot\|init.test" # 查看是否有 service_start_failed、permission denied 等关键词典型错误及解法:
Failed to open '/system/bin/init.test.sh'→ 路径写错,或文件不存在(检查adb shell ls /system/bin/init.test.sh)Cannot execv('/system/bin/init.test.sh')→ 权限不对(chmod 755再试)或shebang路径错误(确认是/system/bin/sh)Permission denied→ SELinux拒绝(见3.3节)Invalid argument→ rc文件语法错误(如多了一个空格、少了一个换行)
3.3 SELinux报错怎么办?两种务实解法
当你在logcat中看到类似avc: denied { execute } for path=/system/bin/init.test.sh的日志,说明SELinux策略阻止了执行。
别急着写te文件——先试试这两个更快的方案:
方案A:临时切换SELinux为permissive模式(仅测试用)
adb shell setenforce 0 adb reboot重启后如果脚本正常运行,证明确实是SELinux拦截。这是最快速的验证手段。
方案B:复用现有SELinux域(推荐长期使用)
在rc文件中,把seclabel行改成:
seclabel u:object_r:shell_exec:s0shell_exec是系统预置的、允许执行shell脚本的domain,绝大多数设备都开放了对它的execute权限。只要你的脚本不访问敏感资源(如/dev/block、/sys/fs/selinux),这个配置足够安全且无需额外策略。
注意:不要用
u:r:init:s0或u:r:kernel:s0等高权限domain,存在安全风险。
3.4 日志看不到?换个缓冲区或加调试输出
Android logcat默认只显示main、system、radio缓冲区。开机早期的日志在events或kernel缓冲区:
# 查看init相关事件 adb logcat -b events # 查看内核级日志(含SELinux avc) adb logcat -b kernel | grep -i avc # 强制脚本输出到多个地方(增强可观测性) echo "[$(date)] Script running" >> /data/local/tmp/boot_debug.log log -p i -t "boot_debug" "Script running"4. 进阶技巧:让脚本更可靠、更实用
当基础流程跑通后,你可以根据实际需求升级脚本能力。以下是几个高频、低风险、高价值的增强点:
4.1 判断是否首次启动,避免重复执行
有些操作(如初始化数据库、生成密钥)只需做一次。用getprop检查标记:
#!/system/bin/sh if [ "$(getprop sys.boot.test.done)" != "1" ]; then log -p i -t "boot_script" "First boot: doing init tasks..." # 这里放你的初始化命令 setprop sys.boot.test.done 1 else log -p i -t "boot_script" "Not first boot, skip init" fi4.2 等待关键服务就绪后再执行
init.rc中某些service依赖其他服务(如surfaceflinger、media)。用wait_for_prop确保时机:
service test_boot /system/bin/init.test.sh class main user root group root oneshot seclabel u:object_r:shell_exec:s0 # 等待zygote启动完成(表示Framework已就绪) wait_for_prop sys.boot_completed 14.3 支持热更新:脚本内容可远程替换
把核心逻辑抽离到/data分区(可读写),主脚本只负责加载:
#!/system/bin/sh # 主脚本只做一件事:执行/data下的真实逻辑 if [ -f /data/local/tmp/real_boot.sh ]; then /data/local/tmp/real_boot.sh fi这样你无需每次改脚本都重启,adb push新版本到/data即可生效。
5. 总结:你已经掌握的核心能力
回顾一下,你现在完全可以独立完成以下事情:
- 编写一个符合Android规范的开机shell脚本,并手动验证其功能
- 将脚本部署到
/system/bin/并设置正确权限 - 通过
/system/etc/init/xxx.rc方式注册开机service,无需修改主init.rc - 快速定位并解决“脚本不执行”“Permission denied”“日志看不到”等典型问题
- 在不写te文件的前提下,利用
shell_execdomain绕过SELinux限制 - 为脚本增加首次启动判断、服务等待、热更新等实用特性
这些能力足以支撑你完成绝大多数Android系统级自动化任务:日志自动归档、硬件自检、调试开关预置、OTA前数据备份、工厂模式快捷入口等。
记住,系统定制不是玄学。它是一系列可验证、可调试、可回滚的操作。每一次成功的开机脚本,都是你对Android启动流程理解更深一步的证明。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。