新手必看:如何让Linux系统开机自动执行Shell脚本
你刚配好一台Linux服务器,写好了监控脚本、数据同步任务或服务自启逻辑,却每次重启后都要手动运行一遍?别再反复敲命令了——这篇教程专为新手设计,不讲抽象原理,只说“怎么做才能立刻生效”。从Ubuntu 20.04到Debian 12、CentOS 8+,只要用的是systemd(现在几乎所有主流发行版都默认使用),这套方法就完全通用。全程无需编译、不改内核、不装额外工具,5分钟搞定,连chmod和systemctl怎么输都给你标清楚。
我们以一个真实可验证的小目标开始:让系统每次开机后,自动在/usr/local/下生成一个带时间戳的标记文件。成功后,你就能把任何.sh脚本、Python程序甚至Node.js服务加进去——思路一通,百用不愁。
1. 为什么老办法不管用了?
过去在Ubuntu 14.04或CentOS 6上,直接往/etc/rc.local里写命令就能开机运行。但自从systemd成为默认初始化系统后,rc.local默认被禁用,不是文件没了,而是“没人读它”。
你可能会看到/etc/rc.local存在,但里面写着“This script does nothing”;也可能执行sudo systemctl status rc-local发现状态是inactive (dead)。这不是你操作错了,是系统变了——就像手机换了操作系统,旧的快捷方式自然失效。
所以,我们要做的不是“找一个能写命令的地方”,而是“告诉systemd:请在启动完成后,主动去执行这个文件”。
2. 创建systemd服务单元:rc-local.service
systemd通过“服务单元文件”管理所有开机任务。我们要创建一个专门负责调用rc.local的服务,让它和传统习惯无缝衔接。
2.1 新建服务定义文件
打开终端,执行以下命令:
sudo vim /etc/systemd/system/rc-local.service小提示:如果你不熟悉vim,可以用
sudo nano /etc/systemd/system/rc-local.service替代,按Ctrl+O保存,Ctrl+X退出。
将下面这段内容完整复制粘贴进去(注意大小写和空格):
[Unit] Description=/etc/rc.local Compatibility ConditionPathExists=/etc/rc.local [Service] Type=forking ExecStart=/etc/rc.local start TimeoutSec=0 StandardOutput=tty RemainAfterExit=yes SysVStartPriority=99 [Install] WantedBy=multi-user.target这段配置的意思是:
Description:给服务起个名字,方便识别;ConditionPathExists:只有当/etc/rc.local文件真实存在时,才启用这个服务;Type=forking:告诉systemd,这个脚本会自己“分叉”成后台进程(兼容传统rc.local行为);ExecStart:真正要执行的命令,即运行/etc/rc.local start;RemainAfterExit=yes:即使脚本执行完退出了,systemd也认为服务仍在运行——这是关键,否则systemd会误判为失败。
2.2 验证文件是否创建成功
执行以下命令检查文件是否存在且内容无误:
sudo ls -l /etc/systemd/system/rc-local.service你应该看到类似输出:
-rw-r--r-- 1 root root 327 ... /etc/systemd/system/rc-local.service如果报错“No such file”,说明没保存成功,请重新编辑。
3. 编写并配置rc.local脚本
现在systemd知道该找谁了,接下来就是告诉rc.local“你该干什么”。
3.1 创建rc.local文件
sudo vim /etc/rc.local粘贴以下内容(注意第一行#!/bin/sh -e必须原样保留,一个字符都不能少):
#!/bin/sh -e # # rc.local # # This script is executed at the end of each multiuser runlevel. # Make sure that the script will "exit 0" on success or any other # value on error. # # In order to enable or disable this script just change the execution # bits. # # By default this script does nothing. echo "Linux系统开机自启脚本已成功运行于 $(date)" > /usr/local/startup.log exit 0关键细节:
- 第一行
#!/bin/sh -e:强制使用POSIX shell,并在遇到错误时立即退出;echo行:记录一条带时间戳的日志,用于后续验证;exit 0:必须有!表示脚本执行成功。缺了它,systemd会认为启动失败。
3.2 赋予执行权限
Linux不会自动执行文本文件,必须显式声明“这个文件可以运行”:
sudo chmod +x /etc/rc.local验证权限是否生效:
ls -l /etc/rc.local正确输出中应包含-rwxr-xr-x(其中x代表可执行)。
4. 启用并启动rc-local服务
现在软硬件(配置+脚本)都齐了,只需两步让systemd记住它、并立刻运行一次。
4.1 启用服务(开机自启)
sudo systemctl enable rc-local这条命令的作用是:在systemd启动时,自动将rc-local.service加入multi-user.target的依赖链。相当于给服务挂了个“开机闹钟”。
4.2 立即启动服务(测试当前是否有效)
sudo systemctl start rc-local.service4.3 检查服务状态
sudo systemctl status rc-local.service正常输出中应包含:
Active: active (exited) since ...如果显示failed或inactive,请重点看journalctl日志:
sudo journalctl -u rc-local.service -n 20 --no-pager常见报错及解决:
Permission denied:rc.local没加+x权限,重跑chmod +x;No such file or directory:路径写错,比如/etc/rc.local拼成/etc/rc_local;Failed at step EXEC spawning:脚本第一行#!/bin/sh -e缺失或格式错误(如多了空格、用了Windows换行符)。
5. 验证效果:查看日志文件
执行以下命令,检查是否生成了预期文件:
cat /usr/local/startup.log你应该看到类似内容:
Linux系统开机自启脚本已成功运行于 Mon Jun 10 14:23:45 CST 2024成功!这证明整个链路——systemd → rc-local.service → /etc/rc.local → echo命令——全部打通。
小技巧:你可以随时修改
/etc/rc.local里的echo命令,再执行sudo systemctl restart rc-local.service立即测试新逻辑,无需重启机器。
6. 扩展实践:运行你自己的Shell脚本
现在rc.local只是一个“启动索引”。真正的业务逻辑,建议单独写成.sh文件,再由rc.local调用。这样结构清晰、便于调试、更新不伤主配置。
6.1 创建你的业务脚本
假设你想开机自动运行一个备份脚本,存放在/home/ubuntu/backup.sh:
sudo vim /home/ubuntu/backup.sh内容示例(每天凌晨2点前备份/var/www到/backup):
#!/bin/bash # 开机后立即执行一次备份(也可配合cron做定时) mkdir -p /backup cp -r /var/www /backup/www_$(date +%Y%m%d_%H%M%S) echo "Backup completed at $(date)" >> /var/log/backup.log赋予执行权限:
sudo chmod +x /home/ubuntu/backup.sh6.2 修改rc.local调用它
编辑/etc/rc.local,在exit 0之前添加一行:
/home/ubuntu/backup.sh完整rc.local应类似:
#!/bin/sh -e echo "Linux系统开机自启脚本已成功运行于 $(date)" > /usr/local/startup.log /home/ubuntu/backup.sh exit 06.3 重启服务并验证
sudo systemctl restart rc-local.service sudo systemctl status rc-local.service再检查/var/log/backup.log是否生成:
tail -n 5 /var/log/backup.log7. 常见问题与避坑指南
新手最容易卡在这几个地方,我们提前帮你踩平:
7.1 脚本里用绝对路径!
❌ 错误写法:
cd ~/myproject python app.py正确写法:
cd /home/ubuntu/myproject /usr/bin/python3 /home/ubuntu/myproject/app.py原因:开机时没有用户登录环境,~、$HOME、$PATH等变量不可靠。务必用which python3确认解释器绝对路径。
7.2 Python脚本中文乱码?
如果你的Python脚本含中文注释或输出,可能因locale未加载而报错。在脚本开头显式设置:
#!/bin/bash export LANG=en_US.UTF-8 export LC_ALL=en_US.UTF-8 cd /home/ubuntu/myproject /usr/bin/python3 app.py7.3 服务启动太快,依赖服务还没就绪?
比如你的脚本要访问MySQL,但rc.local执行时MySQL还没启动完。解决方案:在rc-local.service的[Unit]段添加依赖声明:
[Unit] Description=/etc/rc.local Compatibility ConditionPathExists=/etc/rc.local After=mysqld.service Wants=mysqld.service然后重载配置:
sudo systemctl daemon-reload sudo systemctl restart rc-local.service7.4 想取消开机自启?一键还原
如果某天你想停用,只需两步:
sudo systemctl disable rc-local sudo systemctl stop rc-local.servicedisable移除开机链接,stop立即终止当前运行。rc.local文件本身不受影响,随时可恢复。
8. 总结:掌握开机自启的核心逻辑
回看整个过程,其实只围绕三个关键动作:
- 定义服务:用
rc-local.service告诉systemd“我要执行什么”; - 编写逻辑:在
/etc/rc.local里写具体命令,它是你的“总控开关”; - 授权与启用:
chmod +x给执行权,systemctl enable挂载到启动链。
你不需要理解systemd的D-Bus通信机制,也不用背诵100个参数。只要记住:服务文件定义“谁来干”,rc.local写“干什么”,chmod和systemctl决定“什么时候干、干不干”。
现在,你可以把任何自动化任务——日志清理、API健康检查、IoT设备初始化、AI模型预热——统统塞进这个框架。它稳定、标准、不依赖第三方工具,是每个Linux运维者都该掌握的底层能力。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。