尧图精选

Linux运维一键修复与安装脚本实战:从grub修复到LNMP部署

🕒 发布时间:2026/10/1 9:13:43 📁 来源:尧图网络
简介面向Linux运维人员与系统管理员的自动化脚本合集聚焦系统修复与服务器环境一键部署覆盖启动故障、服务异常、软件包冲突及Web和数据库运行环境搭建等常见场景。压缩包共包含19个文件以14个bash脚本为主体辅以2个Markdown文档、2个环境配置txt文件及1个license许可文件整体仅35KB轻量易用。目前已有175人学习下载。脚本按功能划分为repair_scripts与install_scripts两类涉及磁盘检查、网络配置、日志清理、时间同步、文件浏览器部署及R语言、C、Python等开发环境安装体现了权限控制、错误处理与日志记录等运维最佳实践。既适合初学者快速搭建服务器环境也便于运维人员批量修复或部署时参考复用是一套实用性与可扩展性俱佳的系统管理工具包。1. 一键修复与安装脚本Linux 运维里最被低估的一份“后悔药”说实话Linux 服务器出故障的时候绝大多数人不是不会修而是没时间回忆那条早就被忘干净的修复命令。我见过太多同事半夜对着 grub 黑屏、MySQL 起不来、磁盘挂载失败干瞪眼最后靠百度拼命令越拼越乱。这套一键修复与安装脚本就是把「修复系统引导、重置文件权限、切软件源、装 LNMP 环境」这类高频运维动作全部封装成可重复执行的脚本包。它的价值不在于命令多高深而在于把那些你一年才碰一次的修复流程固化成了确定性的步骤。适合手里维护三五台以上 Linux 服务器、或者正在系统学运维、还没积累起自己的脚本库的人也适合在虚拟机里反复折腾系统、装完又搞坏的操作系统实验党。2. 从读懂脚本包开始先看结构再决定要不要跑拿到这类脚本包第一步不是双击执行而是先把目录结构和执行机制看明白。一键脚本的本质是「把一连串你本来要手动敲的命令按固定顺序、带错误检查地批量执行」。如果连脚本里每条命令干什么、出错会怎样都不知道那这个「一键」就是颗不定时炸弹。2.1 一个典型的 Linux 脚本包文件结构长什么样我以前拆过一个在同类型的脚本包目录组织大致是下面这个样子这套资源的结构和它基本同源linux-repair-env/ ├── check/ # 环境检测脚本跑其它脚本前先过一遍 │ ├── check-cpu-mem.sh │ └── check-disk.sh ├── repair/ # 系统修复类 │ ├── repair-boot.sh # grub 引导修复 │ ├── repair-fs.sh # 文件系统与权限修复 │ └── repair-repo.sh # apt/yum 软件源修复 ├── install/ # 服务器环境安装类 │ ├── install-nginx.sh │ ├── install-mysql.sh │ └── install-redis.sh ├── lib/ # 公共函数库 │ └── common.sh ├── logs/ # 运行时生成记录每次执行日志 └── backup/ # 配置备份目录改文件前先备份这个结构的设计思路很清晰check目录负责预检repair目录管修复install目录管新环境部署lib是公共函数库。拆开看绝大多数脚本的核心逻辑都集中在lib/common.sh里——日志函数、错误退出函数、备份函数都在这里。你后续想改脚本第一个要读的就是lib/common.sh而不是某个具体的修复脚本因为所有脚本都依赖它。2.2 脚本执行的前提root 权限、set -e 与变量保护这类脚本基本默认你用 root 跑。为什么因为修复引导、改 /etc/fstab、装系统级软件包普通用户根本就没有写权限。脚本开头一般长这样#!/bin/bash # 遇到未定义变量报错管道中任一命令失败即整体失败 set -euo pipefail # 非 root 直接退出 if [ $(id -u) -ne 0 ]; then echo 请以 root 身份运行此脚本 exit 1 fi # 引入公共函数库 SOURCE_DIR$(cd $(dirname ${BASH_SOURCE[0]}) pwd) source $SOURCE_DIR/../lib/common.sh这段代码里最关键的是set -euo pipefail。set -e让脚本在任意一条命令返回非零退出码时立即终止避免「命令已经错了还在往下执行」set -u把未定义变量的引用变成错误set -o pipefail解决管道符的问题——没有它cmd1 | cmd2即使 cmd1 失败只要 cmd2 成功整个管道的退出码依然为 0这会掩盖掉大量真实错误。如果你是从 shell 脚本入门阶段过来的我建议你从这一刻起就把这三件套焊死在每个脚本开头。很多所谓「脚本跑了一半没报错但结果不对」的诡异问题十有八九是没开set -e命令失败了照样往下跑最后拿到的是一堆残缺状态。2.3 日志与备份一键脚本里最容易忽略却最要命的部分新手看脚本只看「命令写没写对」老手看脚本先看「有没有日志和备份」。没有日志的修复脚本就是黑匣子——执行完你根本不知道哪一步成功、哪一步跳过、哪一步失败。这套脚本包里公共函数库对日志处理的一致性很重要我一般习惯这样封装# lib/common.sh 片段 LOG_DIRlogs LOG_FILE$LOG_DIR/$(date %Y%m%d-%H%M%S).log BACKUP_DIRbackup/$(date %Y%m%d-%H%M%S) log() { echo [$(date %Y-%m-%d %H:%M:%S)] $* | tee -a $LOG_FILE } die() { log ERROR: $* exit 1 } backup_file() { local file$1 mkdir -p $BACKUP_DIR cp -a $file $BACKUP_DIR/ 2/dev/null || true }注意log()用了tee -a既往日志文件里写又在终端实时打印这样排错时能做到「屏幕上看进度、文件里找线索」。backup_file()里那个|| true是刻意的——如果文件不存在备份失败不应该中断整个脚本记一条日志继续跑才是合理行为。备份目录带时间戳保证多次执行不会互相覆盖。3. 系统修复类脚本实战引导、权限、软件源的修复链路系统修复是这套脚本的核心卖点。下面拆三个最常见的场景按「先看原理再给脚本最后说参数」的顺序来。3.1 启动引导修复grub 重装与内核参数服务器开机直接进 grub 命令行、或者重启后找不到内核多半是引导配置损坏。修复脚本的典型逻辑是先探测当前系统用的什么引导再重新生成配置。我看到这套脚本里 repair-boot.sh 的思路基本是这样的# repair-boot.sh 核心片段 # 检测是否安装了 grub2 if [ -d /boot/grub2 ]; then log 检测到 GRUB2重新生成 /boot/grub2/grub.cfg grub2-mkconfig -o /boot/grub2/grub.cfg elif [ -d /boot/grub ]; then log 检测到 GRUB Legacy尝试 update-grub update-grub else die 未检测到可用引导目录请确认 /boot 分区是否挂载 fi # 检测内核是否完整 if [ ! -f /boot/vmlinuz-* ]; then log 内核文件缺失尝试重新安装 kernel 包 # 这里针对 CentOS/RHEL 系列Debian 系需要换成 apt yum reinstall -y kernel-core 2/dev/null || true fi这段逻辑的关键在于「先探测、再动作」。盲目的 grub2-mkconfig 解决不了问题——如果 /boot 分区本身没挂载生成配置毫无意义。所以脚本开头先判断grub2目录存在再判断内核文件存在每一步有明确的失败出口。实际使用中你要注意在虚拟机里装 Linux 出现蓝屏或者引导损坏时这类脚本能帮你找回引导但前提是你没有动过分区表。如果虚拟机的磁盘本身损坏了修引导没有意义得回到虚拟化平台的快照恢复。另外如果你用的是 UEFI 引导而不是 BIOS脚本逻辑完全不同UEFI 机器得操作/boot/efi里的引导变量很多一键脚本在这块边界上处理得比较粗糙需要你手动确认自己的引导模式。3.2 文件系统与权限修复fsck、fstab 与目录权限重置非正常关机、磁盘写满、人为 chmod 误操作这类问题的修复脚本核心就两块文件系统检查和目录权限重置。# repair-fs.sh 核心片段 # 遍历 /etc/fstab 中所有本地分区做只读检查 grep -v ^# /etc/fstab | awk {print $2} | while read -r mountpoint; do [ -z $mountpoint ] continue # 跳过已经挂载且正在使用的分区避免 fsck 破坏数据 if mountpoint -q $mountpoint; then log 跳过 $mountpoint当前已挂载无法安全执行 fsck continue fi log 检查 $mountpoint fsck -y $mountpoint || true done # 重置常见目录权限 log 修复 /home 目录属主 chown -R root:root /home 2/dev/null || true这里最需要你警惕的是fsck的用法。fsck绝不能对已挂载的文件系统执行否则会直接破坏文件系统——这就是前面代码里mountpoint -q判断的意义。现实里很多人在系统启动时报错后没有卸载分区就直接跑 fsck结果把 ext4 超级块跑废了数据全丢。这个脚本里的处理方式是保守且安全的只检查未挂载的分区。目录权限这块chown -R是重武器用之前必须确认目标目录确实需要重置。我就见过有人跑修复脚本把/home下所有用户目录的属主全改成了 root结果所有用户 SSH 登录后连自己的家目录都进不去。正确姿势是先备份当前属主列表再执行修改# 修复前先记录原始属主 ls -ln /home/ backup/$(date %Y%m%d)-home-owner.txt3.3 软件源修复apt/yum 源一键切换软件源出问题最常见的表现是apt-get update卡住、yum install报 404。国内服务器很多会换上阿里云、腾讯云的镜像源这套脚本的 repair-repo.sh 就是干这个的# repair-repo.sh 核心片段针对 Debian/Ubuntu # 备份原源配置 cp -a /etc/apt/sources.list /etc/apt/sources.list.bak.$(date %Y%m%d%H%M%S) # 写入阿里云镜像源这里以 Ubuntu 22.04 (jammy) 为例 cat /etc/apt/sources.list EOF deb https://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse deb-src https://mirrors.aliyun.com/ubuntu/ jammy main restricted universe multiverse EOF # 更新缓存 apt-get update注意这个脚本只适用于 Ubuntu/Debian 系。CentOS/RHEL 系要改的是/etc/yum.repos.d/下的一堆.repo文件并且 7 和 8 的源地址差异很大CentOS 7 的 baseurl 已经迁移到 vault.centos.orgCentOS 8 已经停更得用 Rocky Linux 或者 AlmaLinux 的替代源。如果一个脚本不区分系统版本就统一改源跑完大概率比不跑还糟糕。在这类脚本里系统版本检测应当是第一优先级if grep -qi ubuntu\|debian /etc/os-release; then REPO_TOOLapt elif grep -qi centos\|rhel\|rocky\|alma /etc/os-release; then REPO_TOOLyum else die 未知系统请手动处理软件源 fi这一步判断决定了后续所有命令的走向。在 Linux 运维里没有比「在错误的系统上执行了错误的包管理命令」更常见的翻车现场了。4. 服务器环境安装脚本实战LNMP 环境一键部署的参数化设计如果说修复脚本是「救火」那安装脚本就是「盖房」。这套资源里的 install 目录覆盖面主要是 Nginx、MySQL、Redis 这类服务器常驻服务。安装类脚本的难点不在于命令本身而在于「环境检测、版本选择、参数传递」这三件事。4.1 环境检测与依赖预装跑安装脚本前先过三道检查一个好的安装脚本绝对不是在服务器上无脑apt-get install。它会先做硬件和系统层面的预检避免装到一半机器直接卡死# install/check-env.sh 片段 min_mem1024 # 最低内存 1024MB min_disk5120 # 最低磁盘 5GB mem_total$(free -m | awk /Mem:/{print $2}) disk_total$(df -BG / | awk NR2{print $2} | tr -d G) log 当前内存 ${mem_total}MB磁盘 ${disk_total}GB [ $mem_total -lt $min_mem ] die 内存不足 ${min_mem}MB拒绝安装 [ $disk_total -lt $min_disk ] die 磁盘不足 ${min_disk}GB拒绝安装 # 检查端口占用 for port in 80 3306 6379; do if ss -lnt | grep -q :$port ; then die 端口 $port 已被占用请先释放 fi done这组检查的实际价值在于「失败早发生」。内存 512M 的小机器硬编 MySQL编译阶段就会 OOM到时候系统直接卡死连 SSH 都连不上去。端口检查同样关键默认 80 被 Apache 占着Nginx 装好也起不来。这些检查看起来简单但能把故障发生的时间点提前到「安装之前」而不是「上线之后」。如果你用的是云服务器建议把磁盘检查阈值调得更高一些因为云盘实际可用空间经常被系统镜像占掉一部分。脚本里df -BG拿到的是整数 GiB这个精度对预检足够用了。4.2 编译安装 vs 二进制安装分钟级与小时级的差别这套脚本里安装方式的选择通常会做成参数。我需要先说明一个普遍情况同一套脚本包不同发行版的处理策略不同。有的脚本全用apt/yum装官方预编译包有的会走./configure make make install的编译流程。两种方式的核心差异可以用一个表格说清楚对比项二进制包安装apt/yum源码编译安装安装耗时15 分钟2090 分钟版本新旧取决于发行版仓库通常偏保守可从官网指定任意版本目录结构分散在 /usr/bin、/etc 等标准位置集中在自定义目录如 /usr/local/nginx卸载难度apt remove即可清理需要手动删目录、清环境变量参数调优依赖发行版默认配置编译期可指定线程数、模块我的习惯是生产环境求稳定优先用发行版自带的二进制包需要特定新版本或者要自定义编译参数比如给 Nginx 加--with-http_stub_status_module才走编译。如果你维护的机器配置不高1C2G 这种强烈建议不要编译 MySQL那个编译时间能把你耐心耗尽编译过程中还可能因为内存不足被杀进程。4.3 MySQL、Nginx、Redis 的脚本化安装参数怎么设计这类安装脚本的典型用法是带参数执行而不是直接跑死不让你选。比如./install/install-mysql.sh --version8.0 --port3306 --datadir/data/mysql脚本内部接收参数并做校验# install-mysql.sh 片段 MYSQL_VERSION8.0 MYSQL_PORT3306 MYSQL_DATADIR/data/mysql MYSQL_ROOT_PASSWORD while [ $# -gt 0 ]; do case $1 in --version*) MYSQL_VERSION${1#*} ;; --port*) MYSQL_PORT${1#*} ;; --datadir*) MYSQL_DATADIR${1#*} ;; --root-pass*) MYSQL_ROOT_PASSWORD${1#*} ;; *) echo 未知参数: $1; exit 1 ;; esac shift done # 参数校验 if [[ ! $MYSQL_PORT ~ ^[0-9]$ ]]; then die 端口必须是数字收到的是: $MYSQL_PORT fi这种--keyvalue的参数风格在 Linux 脚本里很常见${1#*}是 bash 参数扩展表示「去掉第一个参数中及其之前的部分」取出等号后面的值。整个while循环其实是遍历所有命令行参数逐个匹配。这种写法的好处是脚本的「输入」完全可预期不会出现参数位置记错的情况。MySQL 8.0 的安装还要注意一点初始 root 账号的认证插件是caching_sha2_password老版本应用连不上经常报Authentication plugin caching_sha2_password cannot be loaded。如果你装的是 8.0需要在安装脚本里明确把 root 的认证方式改回去或者提前告诉使用者这个坑。安装脚本跑完后的典型验证动作各有侧重Nginx 验证nginx -t然后看curl -I 127.0.0.1的返回头、MySQL 验证mysql -u root -p -e select version();、Redis 验证redis-cli ping返回 PONG这些验证步骤应当被直接写进脚本末尾而不是靠人肉再去确认。5. 避坑记录五次翻车换来的五条经验这类脚本包我用过的版本不算少踩过的坑也挺多。下面这五条不是理论推测是我实际遇到过的现象按「现象 → 原因 → 解决」写出来每条都能帮你省下至少半天。5.1 现象脚本在 CentOS 7 上正常Ubuntu 22.04 直接报错脚本在 CentOS 7 上跑得很干净换到 Ubuntu 22.04 上跑到一半报command not found。原因脚本里用了 CentOS 特有的命令比如yum install -y、systemctl enable的某些参数写法在两系 systemd 里存在细微差别。更隐蔽的是CentOS 7 默认的tar版本和 Ubuntu 22.04 的行为也不同解压参数不兼容。解决跑任何脚本前先确认系统版本与脚本预期的发行版一致不一致就做好等价命令映射。比如 CentOS 用yumUbuntu 用apt-get不是简单替换命令名就行源文件位置、配置文件格式都不同。我在脚本里统一加了/etc/os-release判断识别到非预期系统直接拒绝执行宁可多一次人工确认也不闷头跑。5.2 现象MySQL 安装脚本跑完service 起不来报Failed to open file /data/mysql/ibdata1Permission denied原因安装脚本里指定了自定义 datadir 为/data/mysql但只创建了目录没有把属主改成mysql:mysql。MySQL 进程在 systemd 里以 mysql 用户运行对 datadir 没有写权限自然没法初始化。解决装完后手动执行chown -R mysql:mysql /data/mysql。我从那次以后看到所有涉及自定义数据目录的安装脚本第一件事就是检查目录属主和权限。如果你用的数据盘是单独挂载的还要确认 systemd 的ProtectSystemstrict参数没有拦住 MySQL 的写入。5.3 现象脚本执行到一半 SSH 断开重跑直接二次故障这是最惊魂的一个。当时在云服务器上跑环境安装SSH 连接闪断。重连后再次执行脚本脚本检测到服务已存在就跳过安装但依赖包缺了一半配置文件被改了一半最终系统处于「半新半旧」状态手工排查花了三个小时。原因脚本缺乏「事务性」思维没有在关键步骤做幂等保护和断点标识。解决我在自己的脚本模板里加入了依赖服务的状态检测——如果检测到端口已经监听、或者关键进程已经存在就先做完整性检查确认二进制和配置文件版本匹配不符合作回滚。这个思路我建议你吸收到自己的执行习惯里那种「路径里还没有目标文件就重跑」的简单判断并不是每次都可靠。5.4 现象分区修复脚本把数据盘搞丢了——fstab 的教训服务器重启后挂载失败跑了一遍修复脚本结果数据盘彻底找不到了。原因后来查清脚本里在修改 fstab 前把原文件备份了但备份逻辑有缺陷——备份之后如果脚本中断fstab 没有恢复重启时系统尝试挂载一个 UUID 已变化的分区卡在紧急模式。解决修改 fstab 这种关键操作必须要有「两步走」的安全网。第一步先备份第二步修改后用mount -a验证所有挂载点无误验证通过再把备份文件保留在/root下不删除。如果mount -a报错立即把备份文件复原。我还额外加了一条规则「任何修改 fstab 的脚本写入后必须执行sync否则掉电可能丢失刚写的配置。」严格来说这属于玄学级别的心得了但确实有用。5.5 现象一键装的 PHP 太老扩展全部装不上用脚本部署 LAMP 环境装完后想装 Redis 扩展pecl install redis直接提示不支持当前 PHP 版本。查了才发现脚本用的是发行版自带的 PHP 7.4而项目代码需要 PHP 8.1。原因脚本为了求稳固定用了系统仓库里最保守的版本没有留出版本选择的开关。解决这类安装脚本在跑之前务必确认语言运行时版本能不能满足你的业务代码需求。MySQL 和 PHP 的版本兼容性问题在真实项目里非常常见——代码里用的是 mysqli系统装的是老版本 PHP或者代码里用了match表达式PHP 版本不够直接语法报错。运行环境不是越新越好但至少要与业务代码的预期版本对得上。6. 把一键脚本改造成自己的维护工具三个值得保留的习惯这套脚本真正用顺以后我不建议你把它当成黑匣子——用一步跑一步出问题就乱抓。我的做法是把这个包里最有价值的模式抽出来改造成自己的运维工具箱。核心是三个习惯。第一个是「幂等设计」。脚本的执行结果不应该依赖于执行次数——同一个脚本跑两次第二次应该和第一次同样干净。实现方式很简单每个安装步骤前先检查目标是否已存在install_nginx() { if command -v nginx /dev/null 21; then log nginx 已存在跳过安装 return 0 fi apt-get install -y nginx }第二个是「每个函数只做一件事」。这套脚本里的公共函数库就是这么组织的——log、die、backup_file各管一段复合操作全部拆成独力函数来编排。你后续加脚本也保持这个风格别图省事写一个五百行的「大统一函数」维护起来真的痛苦。第三个是「定时验证而不是故障时验证」。别等服务器出问题时才想起跑修复脚本。我现在的习惯是每个月月初在闲时给测试机跑一遍脚本包里的 check 脚本主动发现潜在问题。环境安装类脚本也先在虚拟机里完整过一遍确认安装步骤和时间都在预期内再拿到生产环境复用。从那以后我每次在生产环境跑任何脚本都强制先走一遍这套流程先看脚本头部的版本声明和预期系统版本再检查日志目录与备份目录权限最后才允许自己敲下回车。希望帮到你也希望你跑完脚本之后能养成「先备份、再执行、后验证」的习惯——这套脚本可以给你兜底但最靠得住的那道防线永远是你自己的操作纪律。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →