手把手教你配置/etc/rc.local,让脚本随系统启动
你是不是也遇到过这样的问题:写好了自动化脚本,每次重启后却要手动运行?或者部署了一个后台服务,总得登录服务器再敲一遍命令?其实,Linux系统早就为你准备好了“开机自动执行”的能力——通过/etc/rc.local文件就能轻松实现。但很多新手会发现,在较新版本的 Ubuntu(比如 18.04 及之后)上,直接编辑/etc/rc.local并不能生效。这不是你的脚本有问题,而是系统启动机制变了。
别担心,这不是功能被删掉了,只是换了一种更规范、更可控的方式启用它。本文不讲抽象原理,不堆术语,就用最直白的语言、最完整的步骤、最容易复现的操作,带你从零开始配置好rc.local,让它真正“随系统一起醒来”。无论你是刚接触 Linux 的运维新人,还是需要快速落地一个自启任务的开发者,只要照着做,15 分钟内就能看到/usr/local/test.log里那行成功的提示。
全文基于真实环境验证(Ubuntu 22.04 LTS),所有命令可直接复制粘贴,每一步都说明“为什么这么做”和“不做会怎样”,还会提前告诉你几个新手踩过的坑——比如中文路径报错、权限遗漏、服务状态误判等。现在,我们就开始吧。
1. 为什么 /etc/rc.local 默认不工作了?
在 Ubuntu 16.04 之前,系统使用传统的 SysV init 启动方式,/etc/rc.local是一个被默认加载的“万能启动入口”。但从 Ubuntu 16.04 开始,系统全面转向 systemd,而 systemd 默认不再主动读取或执行 rc.local。它不是被删除了,而是被“雪藏”了——就像一个老工具被收进抽屉,需要你亲手拿出来、擦干净、装上手柄,才能继续用。
所以,你编辑了/etc/rc.local却没反应,并不是脚本写错了,而是 systemd 根本没去调用它。解决办法很简单:告诉 systemd,“请把这个旧文件当作一个正式服务来管理”。
这个过程只需要两步核心操作:
- 创建一个 systemd 服务单元文件(
rc-local.service),定义如何运行 rc.local; - 确保
/etc/rc.local本身存在、可执行、且内容规范。
下面我们就一步步完成这两件事。
2. 创建并配置 rc-local.service 服务单元
systemd 通过.service文件来定义服务行为。我们要创建一个专门用于兼容rc.local的服务,让它在多用户模式下自动启动。
2.1 创建服务文件
打开终端,执行以下命令创建服务定义文件:
sudo vim /etc/systemd/system/rc-local.service小提示:如果你不熟悉 vim,可以用
sudo nano /etc/systemd/system/rc-local.service替代,nano 更易上手。
将以下完整内容粘贴进去(注意逐字复制,包括空行和缩进):
[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.target2.2 关键字段说明(不用死记,看懂就行)
ConditionPathExists=/etc/rc.local:表示只有当/etc/rc.local文件真实存在时,这个服务才允许启动。这是安全机制,避免服务找不到目标文件而报错。Type=forking:告诉 systemd,这个脚本会“派生子进程”(即自己启动后就退出,把任务交给后台进程)。这是传统 rc.local 的典型行为。ExecStart=/etc/rc.local start:明确指定执行命令。注意这里带了start参数,是因为老式 rc.local 脚本通常支持start/stop等参数。RemainAfterExit=yes:最关键的一行。它让 systemd 认为:即使/etc/rc.local执行完了、进程退出了,这个服务“依然处于运行状态”。否则 systemd 会立刻标记服务为“已停止”,后续依赖它的服务可能出错。WantedBy=multi-user.target:表示该服务应在标准的多用户命令行模式下启动(也就是你日常使用的非图形界面状态)。
这一步完成后,systemd 就知道了“有这么一个服务,它负责运行 rc.local”,但还缺一个真正的rc.local文件。
3. 创建并初始化 /etc/rc.local 文件
现在我们来创建那个被 systemd 监控的“启动索引文件”。
3.1 创建空白文件并添加基础模板
执行命令:
sudo vim /etc/rc.local粘贴以下标准模板内容:
#!/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 "看到这行字,说明添加自启动脚本成功。" > /usr/local/test.log exit 03.2 为什么必须包含这些内容?
#!/bin/sh -e:声明这是 shell 脚本,且-e表示“任一命令失败则立即退出”,防止错误被忽略。exit 0:绝对不能省略。systemd 依赖这个返回值判断脚本是否成功执行。如果忘了写,服务状态会显示failed,即使日志看起来正常。echo ... > /usr/local/test.log:这是我们的“心跳检测”。它会在/usr/local/test.log中写入一行文字,方便你重启后快速验证是否真的生效。
3.3 设置可执行权限
Linux 不会执行没有“执行权限”的文件。必须显式赋予:
sudo chmod +x /etc/rc.local正确做法:
chmod +x
常见错误:只改了内容,忘了加权限,结果服务启动失败。
4. 启用并启动 rc-local 服务
现在,systemd 已经认识这个服务,rc.local文件也准备就绪。接下来就是“通电开机”的最后一步。
4.1 启用服务(开机自启)
让 systemd 在每次启动时自动加载这个服务:
sudo systemctl enable rc-local这条命令的本质,是创建一个软链接:/etc/systemd/system/multi-user.target.wants/rc-local.service → /etc/systemd/system/rc-local.service
这样,系统一进入multi-user.target,就会顺带启动它。
4.2 立即启动服务(无需重启)
你可以马上测试效果,不用等重启:
sudo systemctl start rc-local.service4.3 检查服务状态
运行以下命令查看是否成功:
sudo systemctl status rc-local.service成功状态特征:
- 第一行显示
active (exited)或active (running)(取决于RemainAfterExit设置) - 最后几行有
Started /etc/rc.local Compatibility - 没有红色的
failed或error字样
常见失败原因:
/etc/rc.local没有+x权限 → 运行sudo chmod +x /etc/rc.local- 文件里漏了
exit 0→ 用sudo vim /etc/rc.local补上 - 脚本里有语法错误(比如中文引号、未闭合括号)→ 用
sudo bash -n /etc/rc.local检查语法
确认状态正常后,再检查日志文件:
cat /usr/local/test.log如果输出看到这行字,说明添加自启动脚本成功。,恭喜,你的rc.local已经活过来了。
5. 把真正想运行的脚本“挂”进 rc.local
现在rc.local是个空壳,它只负责执行自己内部的几行命令。但我们的目标是:让它成为所有自启任务的统一入口。就像一个总开关,按下它,就自动打开你家所有的灯。
5.1 创建你自己的业务脚本(以 Python 为例)
假设你想开机自动运行一个 Python 程序ce.py,它会在当前目录生成一个sb.txt文件。
先创建存放脚本的目录(推荐放在/opt或/usr/local/bin,避免权限问题):
sudo mkdir -p /opt/mystartup cd /opt/mystartup创建 Python 脚本:
sudo vim ce.py内容如下(注意:不要用中文路径,不要在脚本里写中文注释或字符串,除非你明确设置了 UTF-8 环境):
with open("/opt/mystartup/sb.txt", "w") as f: f.write("SB")再创建一个 shell 包装器test.sh,用来调用 Python:
sudo vim test.sh内容:
#!/bin/bash cd /opt/mystartup python3 ce.py exit 0重要提醒:
- 使用
python3而不是python,避免因系统默认 Python 版本不一致导致失败;cd切换到脚本所在目录,确保相对路径正确;- 同样必须以
exit 0结尾。
赋予执行权限:
sudo chmod +x ce.py test.sh5.2 修改 rc.local,调用你的脚本
编辑/etc/rc.local:
sudo vim /etc/rc.local把原来的echo行替换成调用你脚本的命令(放在exit 0之前):
#!/bin/sh -e # ...(前面的注释保持不变) # 在这里添加你的启动命令 /opt/mystartup/test.sh exit 0保存退出。再次确认权限:
sudo chmod +x /etc/rc.local5.3 重新加载并测试
因为修改了服务定义文件,需要通知 systemd 重载配置:
sudo systemctl daemon-reload然后重启服务:
sudo systemctl restart rc-local.service检查是否成功:
sudo systemctl status rc-local.service cat /opt/mystartup/sb.txt如果sb.txt里出现了SB,说明你的 Python 脚本已成功随系统启动。
6. 排查常见问题的实用技巧
即使严格按照步骤操作,也可能遇到意外。以下是几个高频问题和对应解法,帮你少走弯路。
6.1 服务状态显示 active but failed
运行sudo systemctl status rc-local.service后,看到active (exited)但紧接着又出现failed,大概率是rc.local内部某条命令执行出错,但脚本仍返回了 0。
解决方法:
临时关闭-e选项,让错误暴露出来。编辑/etc/rc.local,把第一行改成:
#!/bin/sh然后重启服务并查看详细日志:
sudo systemctl restart rc-local.service sudo journalctl -u rc-local.service -n 20 --no-pager日志里会清晰显示哪一行报错(比如command not found、Permission denied)。
6.2 脚本执行了,但生成的文件不在预期位置
这是因为rc.local是在 root 用户上下文中运行的,它的“当前工作目录”是/root,而不是你编辑脚本时所在的目录。
解决方法:
- 所有路径务必写绝对路径(如
/opt/mystartup/test.sh,而不是./test.sh); - 在 shell 脚本开头强制
cd到目标目录(如cd /opt/mystartup); - Python 脚本中写文件时,也用绝对路径(如
/opt/mystartup/sb.txt)。
6.3 中文字符导致脚本崩溃
如果你的 Python 脚本里写了中文(比如f.write("你好")),而系统 locale 没设置好,就会报UnicodeEncodeError。
解决方法(二选一):
- 方案 A(推荐):避免在开机脚本中使用中文,用英文或 ASCII 字符替代;
- 方案 B:在
test.sh中显式设置语言环境:
#!/bin/bash export LANG=en_US.UTF-8 export LC_ALL=en_US.UTF-8 cd /opt/mystartup python3 ce.py exit 07. 总结:你已经掌握了一个可靠的系统级自动化入口
到这里,你已经完成了整个流程:理解了 systemd 下rc.local失效的原因,亲手创建了服务单元,初始化了启动脚本,挂载了业务逻辑,并掌握了排错的核心方法。这不是一个“一次性的技巧”,而是一个可复用的工程能力——今后任何需要开机自启的任务,无论是备份脚本、监控程序、数据库初始化,还是 Web 服务预热,你都可以用同样的模式接入。
回顾一下关键动作:
rc-local.service是 systemd 的“翻译官”,把老式脚本变成现代服务;/etc/rc.local是你的“总控台”,所有启动命令都从这里出发;chmod +x和exit 0是两个看似微小、实则致命的细节;- 绝对路径、显式
cd、python3替代python,是避免环境差异的黄金习惯。
最后提醒一句:虽然rc.local很方便,但对于长期运行的服务(比如 Web 服务器),更推荐为它单独编写 systemd service 文件。rc.local最适合做“一次性初始化”或“轻量级调度入口”。它的价值,不在于替代专业方案,而在于给你一个简单、稳定、随时可用的起点。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。