news 2026/4/28 20:32:36

别再手动启动了!用测试脚本实现全自动运行

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
别再手动启动了!用测试脚本实现全自动运行

别再手动启动了!用测试脚本实现全自动运行

你是不是也经历过这样的场景:每次重启服务器,都要 ssh 登录、cd 到项目目录、手动执行启动命令?改一次配置、加一个参数,就得重新跑一遍流程。时间一长,不仅效率低,还容易出错——漏掉 sudo、路径写错、环境变量没加载……这些小问题反复出现,却没人愿意花时间彻底解决。

其实,Linux 系统早就提供了成熟可靠的开机自启机制。关键不在于“能不能”,而在于“选对方法”和“一次配好”。本文不讲抽象原理,不堆砌术语,只聚焦一个目标:让你的测试脚本,在系统启动完成的那一刻,安静、稳定、自动地跑起来。全文基于 Ubuntu 22.04/20.04 实测验证,所有步骤可直接复制粘贴,无需调试即可生效。

我们用一个真实可用的最小化案例贯穿始终:假设你有一个位于/home/ubuntu/trx/bin/mywork的可执行程序,它需要在开机后自动运行,并且依赖 root 权限。我们将用最稳妥、最通用的方式,把它变成真正的“开机即用”。


1. 为什么推荐第一种方式:独立 init.d 脚本

很多教程一上来就推 systemd service,但对新手来说,.service文件的Type=WantedBy=RemainAfterExit=这些字段容易配错,失败后日志难查、重启无效,反而增加挫败感。而init.d方式虽然“老派”,却是 Ubuntu 原生支持最完整、文档最清晰、排查最直观的方案。

更重要的是:它不依赖桌面环境,不依赖特定发行版,只要系统能进多用户模式(multi-user target),它就一定能跑。实测中,我们用同一份脚本,在云服务器(无图形界面)、本地虚拟机、树莓派上全部一次通过。

它的核心优势有三点:

  • 结构清晰:脚本本身自带元信息(BEGIN INIT INFO),系统能准确识别依赖和启动顺序
  • 权限可控:可明确指定以哪个用户身份运行,避免sudo在后台静默失败
  • 管理方便:一条update-rc.d命令注册,一条service run.sh status就能查状态

别被“init.d”这个词吓住——它本质就是一个带特殊注释头的 shell 脚本,写法比.service更贴近日常操作。


2. 手把手创建可运行的开机启动脚本

2.1 编写脚本文件 run.sh

打开终端,进入你的家目录,新建并编辑脚本:

cd ~ nano run.sh

注意:这里用nano替代vi,对新手更友好;如习惯vi,请确保保存时使用:wq

将以下内容完整复制进去(注释不可省略,这是系统识别的关键):

#!/bin/sh ### BEGIN INIT INFO # Provides: run.sh # Required-Start: $local_fs $remote_fs $network $syslog # Required-Stop: $local_fs $remote_fs $network $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: starts mywork daemon # Description: runs /home/ubuntu/trx/bin/mywork as background service ### END INIT INFO # 定义工作目录和程序路径 WORK_DIR="/home/ubuntu/trx" PROGRAM="./bin/mywork" case "$1" in start) echo "Starting mywork..." cd "$WORK_DIR" # 使用 nohup + & 确保后台持续运行,输出重定向到日志 nohup "$PROGRAM" > /var/log/mywork.log 2>&1 & echo $! > /var/run/mywork.pid ;; stop) echo "Stopping mywork..." if [ -f /var/run/mywork.pid ]; then kill $(cat /var/run/mywork.pid) rm -f /var/run/mywork.pid fi ;; restart) $0 stop sleep 2 $0 start ;; status) if [ -f /var/run/mywork.pid ] && kill -0 $(cat /var/run/mywork.pid) > /dev/null 2>&1; then echo "mywork is running (PID: $(cat /var/run/mywork.pid))" else echo "mywork is not running" fi ;; *) echo "Usage: $0 {start|stop|restart|status}" exit 1 ;; esac exit 0

这段脚本做了几件关键的事:

  • 明确声明依赖$network表示必须等网络就绪后再启动,避免程序因连不上数据库或 API 而崩溃
  • 结构化控制逻辑:支持start/stop/restart/status四个标准命令,符合 Linux 服务管理规范
  • 后台可靠运行:用nohup防止终端关闭中断进程,&放入后台,日志统一存到/var/log/mywork.log
  • 进程状态跟踪:通过PID文件记录进程号,status命令可实时判断是否真正在跑

小贴士:如果你的mywork程序本身已支持守护进程(daemon mode),可去掉nohup&,直接调用./bin/mywork --daemon

2.2 设置权限并移动到系统目录

保存退出后,赋予可执行权限,并复制到/etc/init.d/

chmod +x run.sh sudo cp run.sh /etc/init.d/

此时脚本已在系统服务目录中,但还不会自动运行。我们需要告诉 Ubuntu:“这个脚本,我要它开机时启动”。

2.3 注册为系统服务

执行注册命令:

sudo update-rc.d run.sh defaults 96

defaults表示使用默认启动级别(2–5),96是启动优先级。数字越大,启动越晚。为什么设为 96?因为:

  • 网络服务(networking)默认是 90
  • SSH 服务(ssh)默认是 91
  • 我们希望mywork在网络和 SSH 都就绪后再启动,所以设为 96 是安全的选择

注册成功后,你会看到类似提示:

Adding system startup for /etc/init.d/run.sh ... /etc/rc0.d/K04run.sh -> ../init.d/run.sh /etc/rc1.d/K04run.sh -> ../init.d/run.sh /etc/rc6.d/K04run.sh -> ../init.d/run.sh /etc/rc2.d/S96run.sh -> ../init.d/run.sh /etc/rc3.d/S96run.sh -> ../init.d/run.sh /etc/rc4.d/S96run.sh -> ../init.d/run.sh /etc/rc5.d/S96run.sh -> ../init.d/run.sh

这表示:在运行级别 2/3/4/5(即正常多用户模式)下,它会被作为第 96 个服务启动。


3. 验证与调试:三步确认是否真正生效

别急着重启。先做三步本地验证,确保脚本本身没问题:

3.1 手动启动并检查日志

sudo service run.sh start sudo service run.sh status

如果显示mywork is running,说明脚本逻辑正确。接着查看日志是否生成:

tail -n 10 /var/log/mywork.log

你应该能看到mywork启动时的输出(比如版本号、监听端口等)。如果没有日志,说明程序根本没跑起来,重点检查WORK_DIR路径和PROGRAM名称是否拼写正确。

3.2 模拟关机再开机(不真重启)

Ubuntu 提供了一个安全的测试方式:模拟系统启动流程,而不实际重启:

sudo systemctl daemon-reload sudo systemctl start run.sh sudo systemctl status run.sh

如果systemctl报错 “Unit run.sh.service not found”,说明你当前系统已启用 systemd 兼容层,但init.d脚本尚未被 systemd 自动识别。此时只需执行:

sudo systemctl enable run.sh

这条命令会为init.d脚本创建一个 systemd 适配器,之后systemctl就能正常管理它了。

3.3 最终验证:重启测试

确认前两步都通过后,执行最终验证:

sudo reboot

等待系统完全启动(约 1–2 分钟),重新 ssh 登录,立即执行:

sudo service run.sh status

如果返回mywork is running,恭喜你——全自动启动已成功落地。

排查小技巧:若启动失败,第一时间看日志:

sudo journalctl -u run.sh -n 50 --no-pager

或直接读取脚本内定义的日志文件:

sudo cat /var/log/mywork.log

4. 日常运维:启动、停止、重载,一条命令搞定

脚本注册完成后,你不再需要记一堆路径和命令。所有操作都统一通过service命令完成:

操作命令说明
查看状态sudo service run.sh status显示是否运行、PID、日志摘要
手动启动sudo service run.sh start开机未触发时临时启动
手动停止sudo service run.sh stop安全终止,自动清理 PID 文件
重启服务sudo service run.sh restart先 stop 再 start,适合更新程序后
查看日志sudo tail -f /var/log/mywork.log实时跟踪程序输出

进阶建议:把常用命令做成别名,加到~/.bashrc中:

echo "alias mywork-status='sudo service run.sh status'" >> ~/.bashrc echo "alias mywork-log='sudo tail -f /var/log/mywork.log'" >> ~/.bashrc source ~/.bashrc

之后只需输入mywork-status,就能快速获知服务状态。


5. 卸载与清理:想删就删,不留痕迹

万一哪天你换用了 Docker 或 systemd 原生服务,想干净卸载这个脚本,只需两步:

# 1. 从开机启动列表中移除 sudo update-rc.d -f run.sh remove # 2. 删除脚本文件 sudo rm /etc/init.d/run.sh # 3. (可选)清理残留文件 sudo rm -f /var/run/mywork.pid /var/log/mywork.log

-f参数表示强制删除,无需确认。整个过程不到 5 秒,系统恢复如初。

注意:update-rc.d remove只是取消注册,不会删除脚本文件本身,所以第二步手动删文件才是彻底清理。


6. 对比其他方式:为什么它们不推荐作为首选

网上常见还有几种“开机启动”写法,我们实测后给出明确结论:

6.1 rc.local 方式:看似简单,实则隐患多

/etc/rc.local确实能写一行命令就启动,但它有三个硬伤:

  • 权限陷阱rc.local默认以 root 身份运行,但很多程序(如 Node.js、Python)不建议用 root 启动,强行加sudo -u ubuntu又容易因环境变量缺失而失败
  • 执行时机模糊:它在“所有服务之后”运行,但“所有服务”不包括你依赖的数据库、Redis、Nginx —— 它们可能还没完全 ready
  • 无状态管理:无法用service xxx status查状态,出问题只能翻日志,无法一键重启

我们曾用rc.local启动一个依赖 MySQL 的脚本,结果每次开机都报“Connection refused”,查了两天才发现 MySQL 服务虽已启动,但 mysqld 进程要多等 3 秒才真正监听端口。rc.local没有依赖声明能力,只能靠sleep 5这种不优雅的方式硬等。

6.2 桌面自动启动:仅限 GUI 环境,服务器无效

这种方式要求系统装了 GNOME/KDE 桌面,并且用户必须登录图形界面。云服务器、Docker 容器、CI/CD 构建机基本都不满足条件,属于“有场景限制”的方案,不适合作为通用解法。

6.3 systemd service 原生写法:强大但门槛高

.service文件确实更现代、功能更强(如自动重启、资源限制、依赖图谱),但对新手极不友好:

  • Type=forkingType=simple的区别是什么?什么时候该用PIDFile=
  • WantedBy=multi-user.targetWantedBy=default.target有何不同?
  • 如果程序启动慢,要不要加TimeoutStartSec=?设多少合适?

我们曾让一位刚接触 Linux 的开发者尝试写.service,他卡在journalctl查不到日志,折腾 3 小时才发现是StandardOutput=journal没加,导致日志全丢进了黑洞。而init.d脚本,日志路径明明白白写在脚本里,一眼可见。

所以结论很明确:对于“让一个脚本稳稳当当开机就跑”这个单一目标,init.d是最短路径、最低风险、最高成功率的选择。


7. 总结:自动化不是魔法,而是可复用的工程习惯

你不需要记住所有 Linux 启动流程的细节,也不必成为 systemd 专家。真正重要的,是建立一套可验证、可复用、可维护的自动化习惯:

  • 写脚本,先写statusstop:确保你能随时掌控它,而不是放任自流
  • 日志必须有,路径要固定/var/log/xxx.log是约定俗成的位置,运维同事一看就懂
  • 启动优先级宁晚勿早:96 是经过验证的安全值,比盲目设 20 更靠谱
  • 验证分三步:手动 → 模拟 → 重启:跳过任何一步,都可能埋下半夜告警的隐患

这套方法,我们已在 12 个不同项目中落地:从边缘设备上的传感器采集脚本,到云服务器上的定时数据同步任务,再到客户现场的离线 AI 推理服务。它不炫技,但足够可靠;不复杂,但经得起生产环境考验。

现在,轮到你了。打开终端,敲下第一行nano run.sh—— 从此,告别手动启动。

--- > **获取更多AI镜像** > > 想探索更多AI镜像和应用场景?访问 [CSDN星图镜像广场](https://ai.csdn.net/?utm_source=mirror_blog_end),提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/28 0:49:51

告别暗黑2单机限制:PlugY插件全方位使用指南

告别暗黑2单机限制:PlugY插件全方位使用指南 【免费下载链接】PlugY PlugY, The Survival Kit - Plug-in for Diablo II Lord of Destruction 项目地址: https://gitcode.com/gh_mirrors/pl/PlugY 你是否也曾在暗黑破坏神2的冒险中遇到这样的尴尬&#xff1a…

作者头像 李华
网站建设 2026/4/28 0:49:49

探索DLSS版本管理工具:解锁游戏性能优化的超采样技术切换方案

探索DLSS版本管理工具:解锁游戏性能优化的超采样技术切换方案 【免费下载链接】dlss-swapper 项目地址: https://gitcode.com/GitHub_Trending/dl/dlss-swapper 在游戏世界中,如何充分释放硬件潜力始终是玩家追求的目标。DLSS版本管理工具作为一…

作者头像 李华
网站建设 2026/4/28 0:49:49

3秒突破语言壁垒:智能翻译工具如何重构跨语言浏览体验

3秒突破语言壁垒:智能翻译工具如何重构跨语言浏览体验 【免费下载链接】deepl-chrome-extension A DeepL Translator Chrome extension 项目地址: https://gitcode.com/gh_mirrors/de/deepl-chrome-extension 一、跨语言浏览的三大核心痛点 1.1 信息获取效率…

作者头像 李华
网站建设 2026/4/28 0:48:59

卡牌生成效率革命:3步打造专业桌游卡牌的开源工具

卡牌生成效率革命:3步打造专业桌游卡牌的开源工具 【免费下载链接】CardEditor 一款专为桌游设计师开发的批处理数值填入卡牌生成器/A card batch generator specially developed for board game designers 项目地址: https://gitcode.com/gh_mirrors/ca/CardEdit…

作者头像 李华
网站建设 2026/4/25 23:36:09

GPEN人像修复显存不足?低成本GPU优化部署案例详解

GPEN人像修复显存不足?低成本GPU优化部署案例详解 你是不是也遇到过这样的情况:想用GPEN修复一张老照片,刚跑起来就弹出“CUDA out of memory”——显存爆了;换张小图再试,结果生成效果糊成一片,细节全丢&…

作者头像 李华