刚试完就成功了!测试开机启动脚本真实反馈
你是不是也经历过这样的时刻:写好一个自动化脚本,满心期待它能在系统重启后自动运行,结果一 reboot,发现什么都没发生?别急,这不是你代码的问题,大概率是启动流程没走对。今天这篇内容,不讲抽象理论,不堆参数配置,就用最直白的语言,复盘我刚做完的一次真实测试——从写脚本、放位置、加链接,到重启验证,全程无坑、一步到位。整个过程不到5分钟,连中间查文档的时间都算上,也才12分钟。
这篇文章不是教科书式的“标准答案”,而是我把操作台前的真实动作、终端里敲出的每条命令、看到的每一行输出,原原本本地记录下来。你会看到:哪里容易手误、哪个提示容易被忽略、为什么选rc5.d而不是rc3.d、软链接名字里的S99到底意味着什么……所有这些,都不靠猜,全靠实测。
如果你用的是 CentOS 或 Ubuntu(包括 Debian 系发行版),那下面的内容,你照着做,大概率也能“刚试完就成功”。
1. 先写一个能被系统认出来的脚本
很多人卡在第一步:脚本写好了,但系统压根不把它当“服务”看。关键不在功能多炫酷,而在格式是否规范。
我写的测试脚本叫mytest.sh,放在/etc/init.d/目录下——这是传统 SysV init 系统认服务的“法定地址”。注意,不是随便哪个目录都行,也不是.sh后缀就能自动生效。
这个脚本内容极简,只做一件事:每次开机时,在/var/log/下生成一个带时间戳的标记文件。这样重启后,只要去翻日志目录,一眼就能确认它有没有跑过。
#!/bin/bash ### BEGIN INIT INFO # Provides: mytest # Required-Start: $local_fs $network # Required-Stop: $local_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Test startup script # Description: A simple script to verify boot execution ### END INIT INFO case "$1" in start) echo "Starting mytest at $(date)" >> /var/log/mytest.log touch /var/log/mytest_started_$(date +%Y%m%d_%H%M%S).flag ;; stop) echo "Stopping mytest at $(date)" >> /var/log/mytest.log ;; restart) $0 stop $0 start ;; *) echo "Usage: $0 {start|stop|restart}" exit 1 ;; esac exit 0为什么必须加
INIT INFO块?
很多教程省略这一步,但 Ubuntu(尤其较新版本)和部分 CentOS 镜像会依赖它来判断脚本是否合规。少了它,update-rc.d可能直接跳过,或者chkconfig报错。这不是可有可无的注释,是 init 系统读取的元数据。
保存后,别忘了给执行权限:
sudo chmod +x /etc/init.d/mytest.sh2. 确认你的系统到底走哪条“启动通道”
Linux 启动时,并不是一股脑把所有脚本全拉起来。它按“运行级别”(runlevel)分组加载,不同级别对应不同用途:比如单用户模式、多用户模式、图形界面模式等。
我们不需要背级别含义,只需要知道一件事:图形桌面环境通常运行在 runlevel 5,纯命令行多用户环境在 runlevel 3,而大多数现代桌面版 Ubuntu 和带 GUI 的 CentOS 默认就是 5。
所以,先敲这一行,看自己系统当前和上次启动的级别:
runlevel我的输出是:
N 5N表示“未切换”(即开机后没手动改过),后面的5就是关键——它告诉我们,系统启动时会去/etc/rc5.d/这个目录里找要执行的脚本。
小知识卡片:
rcx.d是什么?
/etc/init.d/是“脚本仓库”,存所有服务脚本原件;/etc/rc5.d/(x=0~6)是“调度清单”,里面全是软链接,指向/etc/init.d/里的脚本;- 名字以
S开头的,表示“Start”(启动);以K开头的,表示“Kill”(停止);- 后面的两位数字(如
S99)决定执行顺序:数字越小越早执行,越大越晚。数据库服务一般要等网络就绪,所以常设为S99;而基础网络服务可能设为S10。
3. 进入调度目录,准备加链接
既然runlevel显示是5,那就直奔主题:
cd /etc/rc5.d/进来看看现在有什么:
ls -l S*你会看到一堆类似S10sysklogd、S20rsyslog这样的链接。它们都是系统自带服务,按序号排好队,等着开机时被挨个调用。
我们的目标,就是在这里加一个属于自己的链接,名字得符合规则:S+ 两位数字 + 自定义名(比如S99mytest)。数字选99是因为——它足够靠后,确保前面的基础服务(网络、文件系统)都已就绪,不会因依赖未满足而失败。
为什么不用
S01?
曾经我试过S01mytest,结果重启后日志里只有报错:“cannot resolve hostname”、“no route to host”。原因很简单:脚本里如果涉及网络请求或域名解析,它比网络服务还早启动,自然失败。S99不是随意选的,是留足缓冲的安全位。
4. 创建软链接:一行命令,两个路径
现在,回到/etc/rc5.d/目录,执行这条命令:
sudo ln -s /etc/init.d/mytest.sh S99mytest注意三点:
- 必须用
sudo,否则没权限写系统目录; - 源路径
/etc/init.d/mytest.sh要写绝对路径,不能写../init.d/mytest.sh; - 目标名
S99mytest不能带斜杠,就是纯文件名。
执行完,再ls -l S99*看一眼:
S99mytest -> /etc/init.d/mytest.sh箭头右边清晰显示它确实指向了我们刚放好的脚本。这就完成了最关键的“注册”动作。
常见手误提醒:
- 写成
ln -s S99mytest /etc/init.d/mytest.sh(源和目标反了)→ 链接会损坏;- 忘了
sudo→ 权限拒绝,但终端可能不报错,只静默失败;- 脚本名拼错,比如
mytest.sh写成mytest→ 链接存在,但指向无效路径,开机时会报 “No such file”。
5. 验证:不重启,也能先看效果
很多新手一上来就reboot,其实大可不必。我们可以模拟一次启动调用,提前验证脚本逻辑是否通顺:
sudo /etc/init.d/mytest.sh start然后检查日志和标记文件:
tail -n 1 /var/log/mytest.log ls -t /var/log/mytest_started_*.flag | head -n 1如果看到类似:
Starting mytest at Mon Jun 10 14:22:35 CST 2024 /var/log/mytest_started_20240610_142235.flag说明脚本本身完全没问题,执行权限、路径、语法全部过关。这时候再重启,心里就有底了。
6. 重启测试:真刀真枪见分晓
终于到了见证时刻。执行:
sudo reboot等待系统重新启动、登录后,立刻检查:
ls -t /var/log/mytest_started_*.flag | head -n 1如果输出一个新生成的、时间戳明显是本次重启之后的文件名(比如mytest_started_20240610_143822.flag),恭喜你,成功了。
再补一句确认:
grep "Starting mytest" /var/log/mytest.log你会看到至少两行记录:一行是刚才手动start的,另一行是开机自动触发的。时间戳清清楚楚,毫无歧义。
如果没看到?别慌,三步快速定位:
- 先确认
runlevel是否仍是5(有些服务器默认是3);- 检查
/etc/rc5.d/S99mytest是否真实存在且指向正确;- 查看系统启动日志:
sudo journalctl -b | grep mytest,看有没有报错信息,比如“permission denied”或“not found”。
7. 进阶建议:让管理更省心
脚本跑通只是开始。日常维护中,你还会遇到这些问题:怎么临时禁用?怎么查看状态?怎么卸载?这里给出几条轻量但实用的建议:
7.1 用update-rc.d替代手动 ln(Ubuntu 推荐)
Ubuntu 系统提供了更规范的管理方式。如果你用的是 Ubuntu,可以不用手动ln,改用:
sudo update-rc.d mytest.sh defaults 99defaults会自动为 runlevel 2~5 添加S99mytest,为 0/1/6 添加K01mytest(关机时停止)。它比手动建链接更安全,也方便后续移除:
sudo update-rc.d -f mytest.sh remove7.2 给脚本加个状态查询功能
上面的脚本只支持start/stop/restart,但没status。加一行就能让它支持:
status) if [ -f /var/log/mytest_started_*.flag ]; then echo "mytest is running (last started: $(ls -t /var/log/mytest_started_*.flag | head -n1 | cut -d'_' -f3- | sed 's/\.flag//'))" else echo "mytest is not running" fi ;;然后就能用sudo /etc/init.d/mytest.sh status实时查看。
7.3 日志轮转,避免日志撑爆磁盘
长期运行的脚本,日志会越积越多。简单起见,可以在/etc/logrotate.d/下新建一个配置:
sudo tee /etc/logrotate.d/mytest << 'EOF' /var/log/mytest.log { daily missingok rotate 7 compress delaycompress notifempty } EOF这样,日志每天切一份,保留最近7天,自动压缩,完全无感。
8. 总结:一次成功的背后,是四个确定性
这次“刚试完就成功”,不是运气,而是踩准了四个关键确定性:
- 脚本确定性:有标准头、有完整 case 分支、有执行权限;
- 路径确定性:
/etc/init.d/存原件,/etc/rc5.d/加链接,路径一分不差; - 时机确定性:
runlevel确认是5,S99确保依赖就绪; - 验证确定性:重启前手动
start测试逻辑,重启后ls+grep双重确认结果。
你不需要记住所有 runlevel 数字,也不必背熟每个rcx.d目录的用途。只要养成三个习惯:
① 每次操作前,先runlevel看一眼;
② 加链接时,用ls -l立刻确认指向是否正确;
③ 重启后,第一件事不是打开浏览器,而是ls /var/log/翻标记文件。
剩下的,就交给系统吧。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。