每次重启都自动运行,我是怎么做到的
你有没有遇到过这样的情况:写好了一个监控脚本、一个数据采集程序,或者一个服务守护进程,每次服务器一重启,它就“消失”了,得手动再跑一遍?不仅麻烦,还容易遗漏,关键时刻掉链子。其实,让脚本在系统启动时自动运行,并不像听起来那么神秘——它不需要改内核、不用装复杂工具,甚至不需要懂太多底层原理。只要几步清晰的操作,就能让它稳稳地“守在开机第一线”。
这篇文章不讲抽象概念,不堆术语,只说你真正能用上的方法。我会以一个真实可用的镜像“测试开机启动脚本”为蓝本,手把手带你把一个普通 shell 脚本变成开机即启的可靠组件。无论你是刚接触 Linux 的新手,还是习惯用 Docker 却对宿主机启动机制不太熟悉的开发者,都能照着做、马上见效。
整个过程基于标准 SysV init 兼容机制(CentOS 6/7、Ubuntu 16.04 等传统发行版通用),不依赖 systemd 的 unit 文件写法,避免版本差异带来的困惑。所有操作都在终端里敲几行命令,没有图形界面,没有隐藏配置,每一步你都能看到、验证、理解。
1. 先准备好你的脚本:不是随便写个.sh就行
很多人卡在第一步:脚本写好了,但系统根本不认。问题往往出在“格式”和“位置”上——它不只是能执行,还得“符合启动规范”。
1.1 脚本必须放在/etc/init.d/目录下
这是传统 Linux 启动体系约定的“服务脚本仓库”。系统启动时,只会从这里读取可执行文件。所以,不管你原来把脚本放在家目录、/opt还是/tmp,都得先挪过去:
sudo cp /path/to/your/script.sh /etc/init.d/mytest.sh注意:文件名建议用小写字母+数字+下划线,避免空格和特殊符号;后缀
.sh可加可不加,但加上更直观。
1.2 脚本要有标准的“启停框架”
系统不是简单地sh mytest.sh就完事,而是会调用start、stop、status等参数来控制它。所以你的脚本开头要加上标准的 LSB(Linux Standard Base)头注释,并实现基本功能分支。
下面是一个最小可用模板,你可以直接复制保存为/etc/init.d/mytest.sh:
#!/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: 测试开机启动脚本 # Description: 一个简单的开机自启示例,打印时间并写入日志 ### END INIT INFO LOGFILE="/var/log/mytest.log" PIDFILE="/var/run/mytest.pid" case "$1" in start) echo "Starting mytest..." | tee -a "$LOGFILE" echo "$(date): service started" >> "$LOGFILE" echo $$ > "$PIDFILE" ;; stop) echo "Stopping mytest..." | tee -a "$LOGFILE" echo "$(date): service stopped" >> "$LOGFILE" rm -f "$PIDFILE" ;; status) if [ -f "$PIDFILE" ]; then echo "mytest is running (PID $(cat $PIDFILE))" else echo "mytest is not running" fi ;; *) echo "Usage: $0 {start|stop|status}" exit 1 ;; esac exit 01.3 给脚本加上可执行权限
别忘了这关键一步,否则系统连读都读不了:
sudo chmod +x /etc/init.d/mytest.sh现在你可以手动测试一下:
sudo /etc/init.d/mytest.sh start # 启动 sudo /etc/init.d/mytest.sh status # 查看状态 sudo /etc/init.d/mytest.sh stop # 停止如果能看到日志写入/var/log/mytest.log,说明脚本本身完全没问题——接下来就是让它“自动上岗”。
2. 理解系统启动级别:别瞎猜,用命令确认
Linux 启动不是“一股脑全开”,而是按“运行级别(runlevel)”分阶段加载服务。不同级别对应不同用途:单用户模式、多用户文本模式、带图形界面的完整模式等。
而我们的脚本,要放在哪个级别的启动目录里?答案不是靠经验猜,而是用一条命令查出来:
runlevel你会看到类似这样的输出:
N 5其中第二个数字5就是当前系统的默认运行级别(N 表示之前没有运行级别,比如刚开机)。这意味着:系统启动完成后,会进入级别 5,并自动执行/etc/rc5.d/目录下的所有启动脚本。
小知识:
rc0.d~rc6.d是六个运行级别的启动链接目录rcS.d是系统初始化阶段(single-user)用的rc5.d对应图形界面(GUI)模式,rc3.d对应纯命令行(multi-user text)模式- 大多数服务器默认是 3 或 5,桌面环境通常是 5,云服务器常见为 3
你不需要记住所有级别含义,只要记住:runlevel命令输出的第二个数字,就是你要操作的rcX.d目录中的 X。
3. 进入对应的 rcX.d 目录:软链接才是关键
/etc/rc5.d/(或你查到的其他数字目录)里并没有真正的脚本文件,全是指向/etc/init.d/中脚本的软链接。系统按字母顺序执行这些链接,从而决定服务的启动顺序。
先进入目录看看:
cd /etc/rc5.d/ ls -l你会看到一堆以S或K开头的文件,比如:
S10sysklogd S20rsyslog S99apache2 K20mysqlS表示Start(启动)K表示Kill/Stop(停止)- 后面的两位数字(如
99)代表执行顺序:数字越小越早执行,越大越晚
为什么顺序重要?举个例子:如果你的脚本要访问数据库,那它就必须在 MySQL 服务启动之后才运行,否则会失败。这时候你就该给它起个S98mytest这样的名字,确保它排在S97mysql后面。
实用建议:
- 新手起步,用
S99最安全(几乎最后启动,依赖基本都已就绪)- 如果脚本很轻量、无依赖,
S20也可以- 避免用
S01,太早可能网络、磁盘还没准备好
4. 创建软链接:一行命令搞定自动启动
现在,我们把/etc/init.d/mytest.sh“注册”进启动流程。假设runlevel显示的是5,那就操作/etc/rc5.d/:
sudo ln -s /etc/init.d/mytest.sh /etc/rc5.d/S99mytest命令拆解:
ln -s:创建软链接(symbolic link)/etc/init.d/mytest.sh:源文件(真实脚本位置)/etc/rc5.d/S99mytest:目标链接名(必须以S开头 + 两位数字 + 名称)
创建完成后,再ls -l /etc/rc5.d/S99*确认链接是否生效:
S99mytest -> /etc/init.d/mytest.sh如果箭头指向正确,说明注册成功。
注意事项:
- 不要手动生成同名文件(比如直接
touch S99mytest),必须用ln -s- 链接名不能带
.sh后缀(系统不识别)- 如果之前有旧链接,先删掉:
sudo rm /etc/rc5.d/S99mytest
5. 验证与重启:眼见为实
光看链接还不够,得真刀真枪试一次。
5.1 手动触发一次启动流程(不重启)
为了快速验证,可以模拟系统启动时的调用逻辑:
sudo /etc/init.d/mytest.sh start sudo /etc/init.d/mytest.sh status检查/var/log/mytest.log是否有新记录,PID 文件是否生成。
5.2 正式重启测试
这才是最终考验:
sudo reboot等待系统重新启动、登录后,立刻检查:
sudo /etc/init.d/mytest.sh status tail -n 5 /var/log/mytest.log如果显示 “running”,且日志里有重启后的最新时间戳,恭喜你——它真的做到了“每次重启都自动运行”。
6. 常见问题与避坑指南
实际操作中,这几个问题最常绊住人。我把它们列出来,附上一句解决话术,帮你省下两小时排查时间。
6.1 脚本没执行,日志也没更新?
→ 先检查/etc/init.d/mytest.sh是否有可执行权限:ls -l /etc/init.d/mytest.sh,确认有x标志。
→ 再检查软链接是否指向正确路径:ls -l /etc/rc5.d/S99mytest,箭头右边必须是/etc/init.d/mytest.sh(不是相对路径,不是拼写错误)。
6.2 提示 “Permission denied” 或 “No such file or directory”?
→ 很可能是脚本第一行#!/bin/bash写错了,或者用了 Windows 换行符(CRLF)。用dos2unix /etc/init.d/mytest.sh转换,或用vi编辑时输入:set ff=unix保存。
6.3 重启后脚本运行了,但中途崩溃退出?
→ 检查脚本里是否有依赖未就绪。比如访问了/tmp下某个临时文件,但/tmp还没挂载完;或调用了curl却网络还没通。解决方案:加简单等待或重试逻辑,例如:
# 在 start 分支里加 until ping -c1 google.com &>/dev/null; do sleep 2 done6.4 想取消开机启动,怎么删?
→ 只删软链接,别动原始脚本:
sudo rm /etc/rc5.d/S99mytest如果不确定是哪个级别,可以用ls /etc/rc*.d/S99mytest一次性查所有。
7. 进阶提示:不止于“能用”,还要“好管”
当你已经熟练掌握基础流程,可以考虑这几项提升,让自动化更健壮、更易维护:
- 统一管理多个脚本:把所有自启脚本集中放在
/etc/init.d/,用统一命名规范(如app-monitor.sh、log-cleaner.sh),避免混乱。 - 加入日志轮转:用
logrotate配置/var/log/mytest.log,防止日志无限增长占满磁盘。 - 配合监控告警:写个简单检查脚本,每天定时
ps aux | grep mytest,发现异常就发邮件或钉钉通知。 - 迁移到 systemd(可选):如果你用的是较新系统(Ubuntu 18.04+、CentOS 7+),也可以为它写一个
.service文件,功能更强大、依赖管理更清晰。不过本文聚焦“通用稳定”,SysV 方式仍是跨版本兼容性最高的选择。
8. 总结:开机自启的本质,是一次精准的“预约”
回过头看,所谓“每次重启都自动运行”,其实只是做了三件事:
- 把脚本放进系统认可的“服务目录”(
/etc/init.d/) - 告诉系统“我在哪个阶段上岗”(通过
runlevel查出对应rcX.d) - 在那个阶段的“点名册”上留下自己的名字和顺序(用
ln -s创建Sxxname链接)
它不玄乎,也不脆弱。你写的每一行代码、敲的每一条命令,系统都清清楚楚地执行了。没有黑箱,没有魔法,只有清晰的约定和可验证的结果。
下次再遇到需要长期驻留的脚本,别再手动nohup ./xxx.sh &了。花五分钟按这个流程走一遍,它就会成为你服务器里一个沉默却可靠的伙伴——你关机,它待命;你重启,它上岗。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。