开箱即用的自启方案,测试脚本快速落地实践
在日常运维和自动化部署中,经常需要让某些自定义脚本在系统启动时自动运行——比如环境检测、服务预热、日志清理、硬件初始化等。但很多开发者卡在“写好了脚本,却不知道怎么让它开机就跑”这一步。本文不讲理论、不堆概念,直接带你用一套开箱即用的实操路径,把一个普通 Shell 脚本变成真正可靠的开机自启服务。整个过程在 CentOS 和 Ubuntu 上均验证通过,无需额外安装工具,纯系统原生机制,5 分钟内完成从准备到验证。
你不需要懂 init、systemd 或 runlevel 的底层原理,只需要按步骤操作,就能看到效果。文末还会告诉你如何快速排查常见失败场景,避免重启后发现脚本根本没执行的尴尬。
1. 明确目标:我们要做什么
先说清楚结果——我们要实现的是:
系统每次开机完成后,自动执行你写的mytest.sh脚本
脚本以 root 权限运行(保障对系统资源的访问能力)
启动时机可控(不抢在网络、磁盘或数据库之前执行)
失败时有迹可循(能查日志、能手动触发、能快速回退)
这不是“加一行 crontab -e @reboot”的临时方案,而是走系统标准启动流程的正式方式。它稳定、可管理、符合运维规范,也方便后续迁移到 systemd 管理体系。
注意:本文方法适用于传统 SysVinit 风格的启动管理(CentOS 6/7 兼容模式、Ubuntu 16.04–20.04 默认兼容层),也适用于当前仍保留/etc/init.d/和/etc/rc*.d/目录结构的大多数 Linux 发行版。如果你用的是较新版本的 Ubuntu(22.04+)或 CentOS Stream(默认启用 systemd),本文方案依然有效——因为系统会通过sysv-rc-conf或update-rc.d工具自动桥接至 systemd 单元。
2. 准备你的测试脚本
我们从最简单的开始:一个能被系统识别、能记录执行痕迹、能明确反馈状态的脚本。
2.1 编写脚本内容
创建文件/etc/init.d/mytest.sh(注意路径必须是/etc/init.d/):
#!/bin/bash ### BEGIN INIT INFO # Provides: mytest # Required-Start: $local_fs $network $named $time $syslog # Required-Stop: $local_fs $network $named $time $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Description: 测试开机自启脚本 - 记录启动时间并创建标记文件 # Short-Description: mytest startup script ### END INIT INFO case "$1" in start) echo "[$(date)] mytest.sh started" >> /var/log/mytest.log touch /tmp/mytest_ran_at_boot exit 0 ;; stop) echo "[$(date)] mytest.sh stopped" >> /var/log/mytest.log rm -f /tmp/mytest_ran_at_boot exit 0 ;; restart) $0 stop $0 start exit 0 ;; status) if [ -f /tmp/mytest_ran_at_boot ]; then echo "mytest is running (marker file exists)" else echo "mytest is not running" fi exit 0 ;; *) echo "Usage: $0 {start|stop|restart|status}" exit 1 ;; esac关键说明:
- 第一行
#!/bin/bash不可省略,否则系统无法解析### BEGIN INIT INFO块是 SysV 标准注释段,告诉系统该脚本的依赖关系和默认运行级别(2/3/4/5 表示多用户图形/终端模式)start分支里做了两件事:写日志 + 创建临时标记文件,便于后续验证是否真被执行status子命令让你随时检查脚本状态,不用重启也能调试
2.2 设置权限并测试手动运行
sudo chmod +x /etc/init.d/mytest.sh sudo /etc/init.d/mytest.sh start执行后检查:
cat /var/log/mytest.log # 应看到带时间戳的启动记录 ls -l /tmp/mytest_ran_at_boot # 应存在该文件如果这两步都成功,说明脚本本身没有语法或权限问题,可以进入下一步。
3. 查看当前系统的启动级别
Linux 启动时会进入某个“运行级别(runlevel)”,不同级别对应不同的服务集合。我们需要知道系统默认进哪个级别,才能把脚本链接到对应的启动目录。
运行以下命令:
runlevel输出类似:
N 5其中第二个数字5就是当前默认运行级别(N表示此前无运行级别,即刚开机)。
常见含义如下:
| 运行级别 | 含义 |
|---|---|
| 0 | 关机 |
| 1 | 单用户模式(维护用) |
| 2–5 | 多用户模式(含网络、GUI) |
| 6 | 重启 |
对绝大多数桌面/服务器环境,你看到的都是
2、3或5。只要不是0或1,就说明系统支持网络和多用户服务,我们的脚本可以放在这里。
为什么重要?因为系统启动时,会依次读取/etc/rcX.d/目录下的软链接(X 就是运行级别),而这些链接最终指向/etc/init.d/下的真实脚本。
4. 定位并进入对应的 rcX.d 目录
根据上一步得到的运行级别(假设为5),进入对应目录:
cd /etc/rc5.d/这个目录下全是形如Sxxservicename或Kxxservicename的软链接:
S开头:Start,表示开机时启动该服务K开头:Kill,表示关机或切换级别时停止该服务xx是两位数字(01–99):决定执行顺序,数字越小越早执行
你可以用ls -l查看当前已有的链接,例如:
S20network → ../init.d/network S50rsyslog → ../init.d/rsyslog这说明network在rsyslog之前启动(20 < 50)。
小技巧:如果你的脚本依赖网络或数据库,建议选
S90之后的序号(如S99mytest),确保它在基础服务就绪后再运行。
5. 创建启动软链接
现在,我们把/etc/init.d/mytest.sh链接到/etc/rc5.d/,并命名为S99mytest:
sudo ln -s /etc/init.d/mytest.sh /etc/rc5.d/S99mytest注意:
ln -s是创建符号链接,不是复制文件- 源路径
/etc/init.d/mytest.sh必须写绝对路径- 目标名
S99mytest中的S表示启动,99是序号,mytest是可读名称(任意命名,但建议与脚本名一致)
验证是否成功:
ls -l /etc/rc5.d/S99mytest应输出类似:
S99mytest -> /etc/init.d/mytest.sh如果显示No such file or directory,请检查源脚本路径是否拼写正确、是否存在、是否有执行权限。
6. 手动触发启动流程(免重启验证)
别急着 reboot!我们可以模拟系统启动时的调用逻辑,立即验证配置是否生效:
sudo /etc/init.d/mytest.sh start或者更贴近真实场景的方式(通过 rc.d 机制触发):
sudo /etc/rc5.d/S99mytest start然后检查效果:
cat /var/log/mytest.log # 是否新增了时间戳记录? ls -l /tmp/mytest_ran_at_boot # 文件是否存在?如果一切正常,说明软链接和脚本逻辑完全就绪,接下来只需一次重启即可完成闭环。
7. 重启验证与结果确认
执行重启命令:
sudo reboot等待系统重新启动并登录后,立即检查:
# 查看日志是否包含开机时的记录 grep "mytest.sh started" /var/log/mytest.log # 检查标记文件是否由开机过程创建(而非你手动运行) stat /tmp/mytest_ran_at_boot | grep "Modify:"如果日志中有早于你本次登录时间的记录,且Modify:时间接近系统启动时间(可通过uptime对比),就说明脚本确实在开机阶段被执行了。
成功标志:
/var/log/mytest.log中有带时间戳的started行/tmp/mytest_ran_at_boot文件存在且修改时间 ≈ 系统启动时间sudo /etc/init.d/mytest.sh status返回 “is running”
8. 常见问题与快速排障指南
实际落地中,80% 的失败不是脚本写错了,而是环境或权限细节被忽略。以下是高频问题及解法:
8.1 脚本没执行,日志为空
- 检查
/etc/rc5.d/S99mytest是否真实存在且指向正确路径(ls -l) - 检查
/etc/init.d/mytest.sh是否有+x权限(ls -l /etc/init.d/mytest.sh) - 检查脚本第一行是否为
#!/bin/bash(不能是#!/bin/sh且脚本用了 bash 特性) - 检查
Default-Start行是否包含你当前的运行级别(如5)
8.2 脚本执行了,但功能异常(如无法写日志、找不到命令)
- 所有命令使用绝对路径:
/bin/echo、/usr/bin/touch,避免$PATH环境变量未加载 - 在脚本开头添加
set -x(调试模式),将执行过程输出到/var/log/mytest.log,便于定位卡点 - 避免依赖尚未启动的服务(如 MySQL、Docker),可在
Required-Start中显式声明依赖,或改用sleep 10延迟执行(仅临时调试)
8.3 Ubuntu 22.04+ 提示 “update-rc.d: error: cannot find LSB header”
- 这是因为脚本缺少标准 LSB 注释块(即
### BEGIN INIT INFO部分) - 请严格按本文第 2.1 节提供的模板编写,包括所有
#行和空行 - 可用
insserv --dry-run /etc/init.d/mytest.sh验证 LSB 头是否合规
8.4 想取消自启?一键移除
sudo rm /etc/rc5.d/S99mytest # 如果还创建了其他级别链接(如 rc3.d),一并删除 sudo rm /etc/rc3.d/S99mytest无需卸载或禁用,删链接即生效。
9. 进阶提示:从这里出发还能做什么
这套机制只是起点。当你熟悉了脚本注册流程,就可以轻松扩展:
- 把 Python/Node.js/Java 程序包装成 init.d 脚本(只需在
start分支中调用对应解释器) - 📦 用
update-rc.d mytest defaults(Debian/Ubuntu)或chkconfig --add mytest(CentOS)替代手动 ln,更规范 - 🧩 结合
systemctl enable mytest.service(新建 systemd 单元),平滑过渡到现代服务管理 - 在脚本中加入健康检查逻辑,失败时自动发邮件或写入监控平台
最重要的是:你已经掌握了 Linux 启动生命周期中最实用的一环——让自己的逻辑无缝融入系统节奏。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。