再也不用手动跑脚本,这个设置太实用了
你是不是也经历过这样的场景:写好了一个数据采集脚本、一个日志清理工具,或者一个定时监控程序,每次重启服务器后都得手动登录、cd到目录、再敲一遍python monitor.py?重复操作不仅费时,还容易遗漏——尤其在多台设备上部署时,光是检查是否启动就让人头疼。
其实,Linux系统早就有成熟的机制来解决这个问题。只是很多人卡在“不知道从哪下手”或者“试了几次没成功就放弃了”。今天这篇内容,不讲抽象原理,不堆参数配置,就用最直白的方式,带你把“开机自动运行脚本”这件事真正落地。整个过程只需要6步,每一步都有明确指令、常见坑点提醒和验证方法,哪怕你刚接触Linux命令行,也能照着做完、立刻见效。
我们用的是Ubuntu 18.04及后续版本(如20.04、22.04)通用的systemd方案,它比老版本的rc.local更稳定、更可控,也更容易排查问题。重点来了:这不是教你怎么“凑合用”,而是帮你搭一条真正可靠、可维护、能长期运行的自动化通道。
1. 为什么老办法不管用了?
在Ubuntu 14.04时代,大家习惯直接编辑/etc/rc.local文件,在里面加一行python /path/to/script.py,保存后重启就能生效。但到了Ubuntu 18.04,这个文件默认不再被系统读取——不是删了,而是“被禁用了”。
原因很简单:Ubuntu切换到了systemd作为初始化系统,而rc.local只是一个兼容性遗留接口,需要显式启用才能工作。很多教程只说“改一下rc.local就行”,却没告诉你:光改文件内容没用,必须先让系统认识它、信任它、愿意执行它。
这就像给快递员留了一张纸条说“请把包裹放门口”,但如果没告诉他你是谁、门锁密码是多少、甚至没给他开门的权限,那张纸条就只是废纸。
所以,我们的第一步,不是写脚本,而是给系统发一张“通行证”。
2. 创建systemd服务单元:让rc.local重新被识别
systemd通过“服务单元文件”来管理各类启动任务。我们要做的,就是为rc.local创建一个专属的服务定义,告诉systemd:“这个文件是可信的,允许它在多用户模式下运行。”
2.1 新建rc-local.service文件
打开终端,执行以下命令:
sudo vim /etc/systemd/system/rc-local.service小提示:如果你不熟悉vim,可以用
sudo nano /etc/systemd/system/rc-local.service替代,nano更直观,按Ctrl+O保存,Ctrl+X退出。
2.2 粘贴服务配置内容
把下面这段内容完整复制进去(注意:不要漏掉空行和缩进):
[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这段配置的意思是:
- 这个服务叫
rc-local,作用是兼容旧版rc.local; - 只有当
/etc/rc.local这个文件真实存在时,才尝试启动; - 启动方式是“forking”(后台进程模式),适合shell脚本;
- 不设超时限制(
TimeoutSec=0),避免脚本稍慢就被系统杀掉; RemainAfterExit=yes表示:即使脚本执行完了,也认为服务仍在运行——这对rc.local这种“一次性执行”的场景很关键;- 最后一句说明:它应该在系统进入标准多用户状态(即我们日常使用的图形或命令行环境)时被拉起。
2.3 验证文件是否创建成功
执行以下命令查看文件是否存在且内容正确:
ls -l /etc/systemd/system/rc-local.service你应该看到类似输出:
-rw-r--r-- 1 root root 327 Jun 15 10:20 /etc/systemd/system/rc-local.service如果文件大小接近320字节,说明内容基本完整。下一步,我们来准备那个核心载体——rc.local本身。
3. 编写并配置rc.local:你的启动总控台
rc.local在这里不直接写业务逻辑,而是作为一个“启动索引”——就像电脑桌面的快捷方式,它不干活,但它知道该让谁干活、怎么干。
3.1 创建rc.local文件
sudo vim /etc/rc.local3.2 填入标准模板(含关键细节)
粘贴以下内容(特别注意第一行#!/bin/sh -e和最后一行exit 0,缺一不可):
#!/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 "看到这行字,说明rc.local已成功加载。" > /usr/local/test.log exit 0关键细节说明:
#!/bin/sh -e:指定解释器为sh,并开启“出错即停”模式(-e)。这意味着只要某一行命令失败(返回非0值),整个脚本就会立即终止,避免错误被掩盖;exit 0:必须有!这是告诉systemd“我执行成功了”。没有它,systemd会认为脚本异常退出,后续步骤可能不触发;echo ... > /usr/local/test.log:这是我们的“心跳检测”。只要这个文件被成功写入,就证明整个链路通了。
3.3 赋予执行权限
sudo chmod +x /etc/rc.local这一步极其重要。Linux不会执行一个没有“可执行位”的文件,哪怕它语法完全正确。你可以用ls -l /etc/rc.local确认输出中包含x(如-rwxr-xr-x)。
4. 启用并启动服务:让设置真正生效
现在,systemd已经知道rc-local.service的存在,也知道/etc/rc.local是它的执行入口。接下来,我们要正式“激活”它。
4.1 启用服务(开机自启)
sudo systemctl enable rc-local这条命令的作用是:在系统启动流程中,为rc-local.service创建一个软链接,确保它被纳入启动序列。执行后你会看到类似提示:
Created symlink /etc/systemd/system/multi-user.target.wants/rc-local.service → /etc/systemd/system/rc-local.service.4.2 立即启动服务(无需重启)
sudo systemctl start rc-local.service4.3 检查服务状态(必做!)
sudo systemctl status rc-local.service正常情况下,你应该看到:
● rc-local.service - /etc/rc.local Compatibility Loaded: loaded (/etc/systemd/system/rc-local.service; enabled; vendor preset: enabled) Active: active (exited) since Sat 2024-06-15 10:25:33 CST; 1min 2s ago Docs: man:systemd.special(7) Process: 1234 ExecStart=/etc/rc.local start (code=exited, status=0/SUCCESS)重点关注三处:
enabled:说明已设为开机启动;active (exited):说明服务已成功运行完毕;status=0/SUCCESS:说明脚本执行无报错。
如果看到failed或inactive,别急着重来,先看下一条。
4.4 查看日志定位问题(排错利器)
如果状态异常,用这条命令看详细报错:
sudo journalctl -u rc-local.service -n 20 --no-pager它会显示最近20行该服务的日志。常见错误包括:
/etc/rc.local没有执行权限(Permission denied);- 脚本里调用了不存在的命令(比如写了
python3但系统只有python); - 路径写错(比如
/home/user/script.py实际是/home/ubuntu/script.py); - Python脚本里有中文字符但没声明编码(Python 2尤其敏感)。
5. 把你的脚本真正挂上去:从测试到生产
现在,rc.local已经能稳定运行了。下一步,就是让它去调用你自己的业务脚本。
5.1 创建你的业务脚本(以Python为例)
假设你想开机自动运行一个叫data_collector.py的程序,放在/opt/myapp/目录下。
先创建脚本文件:
sudo mkdir -p /opt/myapp sudo vim /opt/myapp/data_collector.py写一个极简测试版:
# /opt/myapp/data_collector.py with open("/tmp/collector_ran.txt", "w") as f: f.write("采集器已在开机时自动运行!")5.2 创建一个包装shell脚本(推荐做法)
不建议直接在rc.local里写python /opt/myapp/data_collector.py,因为环境变量、工作目录、Python路径都可能和你手动执行时不一致。更稳妥的方式是写一个中间shell脚本:
sudo vim /opt/myapp/start_collector.sh内容如下:
#!/bin/bash # 切换到脚本所在目录,避免相对路径出错 cd /opt/myapp # 显式指定python解释器(避免系统默认python版本不符) /usr/bin/python3 /opt/myapp/data_collector.py # 记录执行时间,便于后续排查 echo "$(date): collector started" >> /var/log/collector.log赋予执行权限:
sudo chmod +x /opt/myapp/start_collector.sh5.3 修改rc.local,调用你的启动脚本
再次编辑/etc/rc.local:
sudo vim /etc/rc.local在exit 0之前,添加这一行:
/opt/myapp/start_collector.sh完整片段应类似:
#!/bin/sh -e ... echo "看到这行字,说明rc.local已成功加载。" > /usr/local/test.log /opt/myapp/start_collector.sh exit 0保存退出。现在,每次开机,系统都会自动执行start_collector.sh,进而运行你的Python程序。
5.4 验证效果
重启系统:
sudo reboot等机器起来后,检查两个关键文件:
# 看rc.local是否执行成功 cat /usr/local/test.log # 看你的业务脚本是否真的运行了 cat /tmp/collector_ran.txt # 看日志是否记录 tail -n 5 /var/log/collector.log如果三个文件都存在且内容符合预期,恭喜你,自动化通道已经打通。
6. 实用技巧与避坑指南
这套方案看似简单,但在真实环境中,有几个高频问题值得提前了解,帮你省下几小时调试时间。
6.1 环境变量问题:为什么脚本里找不到命令?
你在终端能运行pip list,但rc.local里执行就报command not found?这是因为systemd服务默认不加载用户的shell配置(如.bashrc),PATH路径非常精简。
解决方案:在shell脚本开头显式设置PATH,例如:
#!/bin/bash export PATH="/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin" cd /opt/myapp python3 data_collector.py6.2 权限问题:为什么写不了文件?
rc.local是以root身份运行的,但你的脚本可能想往普通用户目录(如/home/ubuntu/logs/)写日志,而root默认没有该用户的写权限。
解决方案:用sudo -u username切换用户执行,例如:
sudo -u ubuntu /opt/myapp/start_collector.sh6.3 启动时机问题:我的服务依赖网络,但脚本运行时网络还没就绪
rc.local默认在multi-user.target阶段运行,此时网络可能尚未完全配置好(尤其是DHCP获取IP需要时间)。
解决方案:在rc-local.service的[Unit]部分增加依赖声明:
[Unit] Description=/etc/rc.local Compatibility ConditionPathExists=/etc/rc.local After=network-online.target Wants=network-online.target然后重新加载配置:
sudo systemctl daemon-reload sudo systemctl restart rc-local.service6.4 调试技巧:如何快速验证修改是否生效?
不用每次重启。你可以这样模拟一次完整启动流程:
# 重新加载所有unit文件 sudo systemctl daemon-reload # 重启rc-local服务(相当于重走一遍开机流程) sudo systemctl restart rc-local.service # 立即查看结果 sudo systemctl status rc-local.service7. 总结:你真正掌握的不只是一个技巧
到这里,你已经完成了一次完整的Linux开机自启实践。回顾一下,你实际构建的是一个可扩展、可维护、可诊断的自动化基础框架:
- 你学会了systemd服务单元的基本写法,以后可以为任何程序(Node.js、Java、Go)创建专属启动服务;
- 你理解了
rc.local的现代定位——它不是过时的古董,而是灵活的启动调度中心; - 你掌握了环境隔离、权限控制、依赖管理这些工程化必备意识;
- 你拥有了快速验证和精准排错的能力,而不是靠“重启试试看”。
更重要的是,这件事的复利价值极高:一旦配置好,它就默默为你工作数月甚至数年,每天节省的几分钟,累积起来就是几天的开发时间。
下次当你又写好一个新脚本,只需三步:放进固定目录、写个启动包装、加一行调用——再也不用手动跑了。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。