
简介一套面向 Linux 运维人员、系统管理员与初学者的自动化脚本工具集聚焦系统故障修复与服务器环境的一键部署。脚本覆盖 Ubuntu、CentOS、Debian 等主流发行版具备系统检测、修复策略、安装引导、自动配置与用户交互等模块可协助完成包管理器修复、系统文件恢复、引导重置以及 Web/数据库等服务器环境搭建在降低手动操作风险的同时提升效率。资源压缩包共 24 个文件、约 44KB以 17 个 shell 脚本为主体涉及网络配置、日志清理、权限检查、软件源更换等常见运维场景另有少量 yml 编排配置、txt 环境说明、md 文档和 license 许可文件便于按需查阅与二次调整。借助这套脚本读者可获得一批开箱即用的运维工具适合日常巡检、批量初始化服务器以及搭建 FOSSBilling、R、Rust、Jupyter 等常用环境时参考。目前已有 153 人学习适合正在摸索 Linux 自动化运维或希望提升服务器管理效率的读者。1. 一键修复与安装脚本这套方案到底解决什么问题凌晨两点收到告警网站打不开SSH 能连上但 apt update 报 Release 文件已过期yum makecache 显示镜像源 404。这种时候你最需要的不是重新百度“linux 常用命令”而是一套能一条命令把软件源、包管理器、系统依赖全部拉回正轨的脚本。一键修复与安装脚本干的就是这件事把系统修复和服务器环境安装里最高频、最容易敲错的操作固化成经过验证的命令序列跑一遍就能把机器从“半死状态”拉回来或者把一台新机器从裸系统装到能上线。适合自己管着几台服务器的开发者也适合刚接手一批 Linux 机器、想少踩坑的运维新手。本文不会给你一个黑盒而是把脚本怎么设计、模块怎么拆、参数怎么调、哪些位置最容易翻车完整讲清楚。2. 脚本框架与设计思路为什么用 Bash 做一键脚本模块怎么拆2.1 用 Bash 而不是 Ansible 或 Python轻量、无依赖、SSH 直跑常见做法是用 Bash原因很直接一键修复脚本的使用场景往往是系统已经出了问题这时候不一定有 Python 环境更不一定有 Ansible 控制端。Bash 只需要一个 shell 就能跑SSH 连上就能执行不依赖任何额外包。Ansible 适合批量管理几十台机器的标准化交付但对单机故障处理和临时环境安装来说引入它属于杀鸡用牛刀光安装控制端和配置 inventory 的时间手动命令早就敲完了。Python 在文本处理和复杂逻辑上更强但如果你要修复的是一台 Python 本身已经损坏的系统用 Python 写修复脚本就很尴尬。Bash 的另一个好处是贴近 Linux 原生命令你可以直接在脚本里调用 grep、sed、awk、ss、systemctl不需要额外的解释器依赖。对 shell 脚本入门阶段的同学来说Bash 写出的脚本也更容易读懂出问题可以直接在终端里把脚本里的某一行命令复制出来手动执行验证。2.2 目录结构与模块划分主入口加函数库是更稳的拆法不要把所有命令堆进一个几百行的 main.sh这是很多 linux 脚本翻车的起点。我一般会把脚本拆成主入口加 lib 函数库one-click/ ├─ main.sh ├─ lib/ │ ├─ common.sh │ ├─ system_detect.sh │ ├─ repo_fix.sh │ ├─ pkg_fix.sh │ ├─ boot_fix.sh │ └─ env_install.sh ├─ conf/ │ └─ mirror.list ├─ backup/ └─ logs/这样拆的好处是修复和安装两个大方向各自独立系统识别逻辑单独放一个文件后面加新的安装模块不会碰坏修复模块。每个 lib 文件只暴露一到两个公共函数函数名统一加前缀比如fix_repo、fix_pkg、install_lnmp。conf 目录放可替换的配置比如镜像源地址、数据库默认密码策略脚本主体不写死具体值。2.3 主入口与参数解析写好 usage、set 选项和日志函数主入口只做三件事解析参数、调用 lib 函数、统一退出码。先看代码#!/usr/bin/env bash set -o errexit set -o pipefail set -o nounset SCRIPT_DIR$(cd $(dirname ${BASH_SOURCE[0]}) pwd) source ${SCRIPT_DIR}/lib/common.sh source ${SCRIPT_DIR}/lib/system_detect.sh source ${SCRIPT_DIR}/lib/repo_fix.sh source ${SCRIPT_DIR}/lib/pkg_fix.sh source ${SCRIPT_DIR}/lib/env_install.sh usage() { cat EOF 用法: ./main.sh --sysinfo 查看系统信息 ./main.sh --fix-repo 修复软件源 ./main.sh --fix-pkg 修复包管理器 ./main.sh --install-lnmp 安装 LNMP 环境 ./main.sh --install-docker 安装 Docker EOF } main() { case ${1:-} in --sysinfo) show_sysinfo ;; --fix-repo) fix_repo ;; --fix-pkg) fix_pkg ;; --install-lnmp) install_lnmp ;; --install-docker) install_docker ;; *) usage; exit 1 ;; esac } main $三个set -o是重点。errexit让脚本在任意命令返回非零时立刻退出避免一条命令失败后继续往下跑把系统改得更乱pipefail保证管道中只要有一段失败就算失败nounset会在引用未定义变量时报错直接退出。${1:-}的意思是第一个参数若为空就用空字符串防止无参数执行时报 unbound variable。每个子命令对应 lib 里的一个函数函数返回值就是脚本退出码方便外面用echo $?判断成败。2.4 系统识别不能只靠 unameos-release 才是可靠依据很多一键脚本翻车都翻在系统识别上。uname -s只能告诉你内核叫 Linux但同样叫 LinuxCentOS 用 yumUbuntu 用 aptDebian 用 apt 但源格式又不一样。正确做法是读/etc/os-releasedetect_os() { if [ -r /etc/os-release ]; then . /etc/os-release OS_ID${ID:-unknown} OS_VERSION_ID${VERSION_ID:-unknown} else OS_ID$(uname -s) OS_VERSION_IDunknown fi case ${OS_ID} in ubuntu|debian) PKG_MGRapt ;; centos|rhel|rocky|almalinux|anolis|openeuler) PKG_MGRyum ;; *) PKG_MGRunknown ;; esac }/etc/os-release里的ID字段是发行版的正式标识比 uname 靠谱得多。注意. /etc/os-release这个写法是 source 该文件里面都是KEYVALUE格式的赋值语句source 后就能直接引用变量。后续所有修复和安装逻辑都走PKG_MGR分支而不是假设全天下都是 apt 或都是 yum。国产的 Anolis、openEuler 系统在 os-release 里有自己的 ID这个 case 要提前覆盖否则一键脚本在国产系统上很容易栽跟头。3. 系统修复模块软件源、包管理器、引导与依赖的修复路线3.1 软件源失效修复备份、替换、缓存重建三步走软件源出故障是最常见的服务器问题表现是apt update拉取 Release 文件失败或者yum makecache报 404。修复的前提是先搞清楚是源地址失效还是网络不通脚本里先做连通性检查再动手替换。核心流程分三步备份原源文件、写入新源配置、重建缓存。fix_repo() { detect_os if [ ${PKG_MGR} apt ]; then TS$(date %Y%m%d-%H%M%S) cp -a /etc/apt/sources.list ${BACKUP_DIR}/sources.list.${TS} 2/dev/null || true cp -a /etc/apt/sources.list.d ${BACKUP_DIR}/sources.list.d.${TS} 2/dev/null || true echo deb https://mirrors.aliyun.com/ubuntu/ $(lsb_release -cs) main restricted universe multiverse /etc/apt/sources.list echo deb https://mirrors.aliyun.com/ubuntu/ $(lsb_release -cs)-updates main restricted universe multiverse /etc/apt/sources.list apt-get update elif [ ${PKG_MGR} yum ]; then cp -a /etc/yum.repos.d ${BACKUP_DIR}/yum.repos.d.${TS} sed -i s|^mirrorlist|#mirrorlist|g /etc/yum.repos.d/CentOS-*.repo sed -i s|^#baseurlhttp://mirror.centos.org|baseurlhttps://mirrors.aliyun.com|g /etc/yum.repos.d/CentOS-*.repo yum clean all yum makecache else log ERROR 不支持的发行版 return 1 fi }备份是最重要的一步没有后悔药。替换源时用$(lsb_release -cs)动态取 Ubuntu 的代号避免写死 jammy 或 noble因为同一套脚本要覆盖多个版本。sed 替换 yum 源时先注释掉mirrorlist再把baseurl里的官方镜像地址替换成可用的镜像地址这是 CentOS 源失效的标准处理手法。注意 apt 和 yum 的源配置文件路径不一样见下表系统源配置路径缓存重建命令Ubuntu / Debian/etc/apt/sources.list 及 sources.list.d/apt-get updateCentOS 7/etc/yum.repos.d/CentOS-*.repoyum makecacheRocky / AlmaLinux/etc/yum.repos.d/rocky*.repodnf makecacheopenEuler/etc/yum.repos.d/openEuler.repodnf makecache3.2 包管理器损坏修复APT 锁、dpkg 中断、RPM 数据库重建APT 最常见的故障状态是上一次安装中断锁文件没释放跑任何 apt 命令都提示Could not get lock。修复前必须确认没有其他 apt 进程正在跑直接删锁是危险操作fix_apt() { log INFO 检查是否有 apt 进程占用锁 if pgrep -f apt-get|apt /dev/null 21; then log ERROR 有 apt 进程在运行请等待完成或手动处理 return 1 fi rm -f /var/lib/dpkg/lock-frontend rm -f /var/lib/dpkg/lock rm -f /var/lib/apt/lists/lock rm -f /var/cache/apt/archives/lock dpkg --configure -a apt-get -f install -y apt-get update }先用pgrep检查有没有正在跑的 apt确认没有才清理锁文件。dpkg --configure -a重新配置所有未完成的安装包apt-get -f install -y修复依赖关系。这一步之后绝大多数卡死状态都能恢复。CentOS 系的 RPM 数据库损坏表现不同yum报db5 error或者rpmdb: unable to open重建方式如下fix_yum() { yum clean all 2/dev/null || true rm -f /var/lib/rpm/__db.* rpm --rebuilddb yum makecache }rm -f /var/lib/rpm/__db.*删除的是 RPM 数据库的临时锁文件不是数据库本体删完再rpm --rebuilddb重建索引。前提是你手上没有其他 yum 进程在运行。修复后跑一次yum makecache验证源和数据库都可用。3.3 引导修复GRUB 重装与内核参数回滚如果服务器开机直接掉进 grub rescue 命令行这个场景不适合在远程直接操作因为你连系统都进不去。这一节的脚本逻辑要设计成可救援模式执行的而不是生产环境无脑跑。引导修复的核心是先切到系统根目录再重装 GRUBmount /dev/sda1 /mnt mount --bind /dev /mnt/dev mount --bind /proc /mnt/proc mount --bind /sys /mnt/sys chroot /mnt /bin/bash # 以下命令在 chroot 环境内执行 grub2-install /dev/sda grub2-mkconfig -o /boot/grub2/grub.cfg exit umount /mnt/sys /mnt/proc /mnt/dev umount /mnt这段的注意点在分区。/dev/sda1得替换成你实际的根分区如果是 UEFI 引导还要把 EFI 分区挂到 /mnt/boot/efi然后grub2-install时要加--targetx86_64-efi。判断 BIOS 还是 UEFI 看/sys/firmware/efi是否存在。引导修复成功后的验证方法是在 chroot 里确认/boot/grub2/grub.cfg已经生成并且文件里能查到当前内核版本对应的 menuentry。3.4 依赖修复与 SSH 误改后的自救依赖损坏的典型场景是/usr/lib下的动态库被覆盖或者某个软件卸载时把共享库一起卸了。先看系统里哪些关键命令还能用再排查动态库ldd /bin/ls 21 | grep not found ldconfigldd输出如果出现not found说明该命令依赖的动态库缺失。先用ldconfig重建缓存看能不能找回默认路径下的库不行就用rpm -qf /lib64/libc.so.6或dpkg -S查这个库属于哪个包重新安装那个包。不要盲目apt-get -f install它会自动装一堆额外依赖有时候反而把环境弄得更乱。SSH 误改的修复场景更常见。sshd_config里禁用了密码登录结果秘钥又没配对直接进不去机器。如果还有 console 或云厂商的 VNC 入口可以进去把配置改回来如果只有 SSH 一条路又恰好没断连接千万不要重启 sshd。修复脚本里我一般会放一个生成最小可用 sshd_config 的函数把PasswordAuthentication yes和PermitRootLogin yes写回文件注释掉其他可能引起问题的行。注意这种情况修完后要立刻提醒管理员改回安全策略这个脚本只是救急不是最终安全配置。3.5 修复模块的退出码与验证修复类脚本最容易犯的错误是修完不验证。软件源修完必须确认apt-get update的退出码是 0且日志里没有 404包管理修复完要确认dpkg -l里没有rc状态已移除但配置残留的包。我建议在修复函数末尾加一个验证函数把所有检查项汇总输出verify_fix() { if [ ${PKG_MGR} apt ]; then apt-get update /dev/null 21 echo [OK] apt update || echo [FAIL] apt update dpkg -l | awk $2 ~ /^rc/ {print $2} | head fi }这一步的意义是让脚本自己交代结果而不是等用户去猜。linux 常用命令里的awk、head在这种验证场景里非常实用。修复完的状态必须白纸黑字写在日志里后面排查问题才有据可查。4. 服务器环境安装模块LNMP、Docker、Node、Python 的一键装法4.1 安装前预检端口、磁盘、已有服务探测环境安装和修复不一样修复是出了问题去救安装是把一台干净机器装成可用状态。安装前不检查直接装最常见的结果是 nginx 装完发现 80 端口被占了或者磁盘空间不够编译到一半机器挂掉。预检函数是安装模块的第一道关卡precheck() { log INFO 开始安装前预检 local disk_free disk_free$(df / | awk NR2 {print $4}) if [ ${disk_free} -lt 5242880 ]; then log ERROR 磁盘剩余空间不足 5G终止安装 return 1 fi if ss -lntp | grep -q :80 ; then log WARN 80 端口已被占用nginx 可能需要改端口 fi if command -v nginx /dev/null 21; then log WARN 系统已存在 nginx脚本将跳过安装 fi }df /取根分区剩余磁盘单位是 KB设定 5G 的阈值是经验值因为编译 PHP、MySQL 这类组件时临时文件很容易吃满空间。ss -lntp查端口占用command -v nginx查二进制是否存在这两步配合就能防止重复安装。预检的目的是让脚本知道“该装还是该跳过”而不是无脑执行这也是幂等性的基础。4.2 LNMP 环境安装版本选择逻辑与数据库密码处理LNMPLinux Nginx MySQL/MariaDB PHP是服务器环境安装里最常见的组合。版本选择上有个常见的纠结用包管理器装的版本偏陈旧但稳定编译安装能拿到新版本但一台机器编译 PHP 加 MySQL 耗时四十分钟以上维护成本高。我的建议是默认用包管理器安装稳定版只有业务明确需要新特性时才单独编译。install_lnmp() { detect_os precheck if [ ${PKG_MGR} apt ]; then export DEBIAN_FRONTENDnoninteractive apt-get update apt-get install -y nginx mariadb-server mariadb-client php-fpm php-mysql elif [ ${PKG_MGR} yum ]; then yum install -y nginx mariadb-server php-fpm php-mysqlnd systemctl enable --now php-fpm fi systemctl enable --now nginx systemctl enable --now mariadb secure_mariadb }数据库密码不要写死在脚本里。常见做法是从环境变量读取如果没设置就自动生成一个随机密码安装结束后打印一次secure_mariadb() { local db_pass if [ -z ${MYSQL_ROOT_PASS:-} ]; then db_pass$(tr -dc A-Za-z0-9 /dev/urandom | head -c 16) else db_pass${MYSQL_ROOT_PASS} fi mysql -e ALTER USER rootlocalhost IDENTIFIED BY ${db_pass}; DELETE FROM mysql.user WHERE User; FLUSH PRIVILEGES; log INFO MariaDB root 密码已设置 }用/dev/urandom生成随机密码是安全做法不要用date %s%N | md5sum这类可预测的方式。注意这个函数里mysql命令的密码会出现在日志里所以日志函数在记录这条信息时要做脱敏处理或者干脆不记录密码内容只记录“密码已设置”。4.3 Docker、Node.js、Python 运行时的非交互安装Docker 的安装官方提供了get.docker.com的一键脚本但直接把远程脚本 pipe 给 sh 是不安全的行为。我一般先下载到本地检查脚本内容里的仓库地址没问题后再执行install_docker() { if command -v docker /dev/null 21; then log INFO Docker 已安装跳过 return 0 fi curl -fsSL https://get.docker.com -o /tmp/install-docker.sh chmod x /tmp/install-docker.sh /tmp/install-docker.sh systemctl enable --now docker docker version }docker version在脚本末尾验证安装结果这个命令成功就代表 daemon 在跑、CLI 可用。Node.js 的安装推荐 nvm理由是可以按项目切换版本不污染系统目录install_node() { export NVM_DIR${HOME}/.nvm if [ ! -d ${NVM_DIR} ]; then curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash fi # shellcheck disableSC1091 source ${NVM_DIR}/nvm.sh nvm install --lts nvm alias default lts/* }这里有个高频坑新开终端后 npm 提示找不到命令原因是没有把 nvm 初始化脚本写进~/.bashrc。如果你在 Windows PowerShell 上也会遇到“无法将 npm 项识别为 cmdlet、函数、脚本文件”这类报错本质都是 PATH 里没有 node 的安装目录。在 Linux 上检查which npm和echo $PATH确保 nvm 路径在 PATH 里。Python 的安装相对简单Ubuntu 上的python3-pip直接用 apt 装就行千万不要覆盖/usr/bin/python3因为系统工具如 apt 本身就依赖它。4.4 安装后的自检与开机自启验证安装完不验证等于白装。自检函数要覆盖三个层面服务进程活着、端口在监听、HTTP 请求能通post_check() { systemctl is-active nginx || { log ERROR nginx 未在运行; return 1; } systemctl is-enabled nginx || systemctl enable nginx if ss -lntp | grep -q :80 ; then log INFO nginx 正在监听 80 端口 fi if command -v curl /dev/null 21; then curl -sI http://127.0.0.1 | head -n 1 fi }systemctl is-active查运行状态systemctl is-enabled查开机自启。很多场景下安装服务没问题重启机器后服务起不来问题就出在没设自启。最后用curl -sI发一个 HEAD 请求拿到 HTTP 状态码就证明 Web 服务链路是通的。这四个检查都过了才能通知用户环境装好了。5. 避坑记录一键脚本最容易翻车的五个位置5.1 变量未校验导致 rm -rf 清空根目录现象脚本跑完服务器直接报废所有数据都没了。排查后发现是某次执行时环境变量没有传进来脚本里rm -rf ${TARGET_DIR}/的TARGET_DIR是空的路径变成了rm -rf /。原因脚本在删除前没有校验变量是否非空set -u也没开或者被局部覆盖。解决所有涉及删除的路径先用-n判断变量非空再加目录前缀校验。我现在的写法如下if [ -n ${TARGET_DIR:-} ] [[ ${TARGET_DIR} /tmp/* ]]; then rm -rf ${TARGET_DIR} else log ERROR 删除路径不合法跳过 exit 1 fi这层防护在血泪经验里是最值得用的一条宁可脚本多写几行也不能让 rm 的变量裸奔。5.2 发行版判断靠 uname把 CentOS 的包命令跑到 Ubuntu 上现象一份脚本在 CentOS 上测试通过拿到 Ubuntu 上跑yum 命令不存在脚本报command not found然后因为set -e直接退出用户还以为脚本有问题。原因脚本里没有做发行版识别或者只用了uname -s它返回的是Linux区分不了发行版。解决统一走/etc/os-release的ID字段按PKG_MGR分支执行。具体代码见 2.4不再重复。这个小改动能让一份脚本覆盖大部分主流发行版和国产系统是投入产出比最高的改进。5.3 脚本不幂等跑第二遍直接失败现象第一次安装 LNMP 成功第二次跑同一个脚本报 nginx 已存在、端口被占用、数据库初始化失败。原因脚本里没有做“已存在则跳过”的检查每次都当成全新安装执行。解决安装类函数开头必须检测目标组件是否已存在。代码里我已经用了这样的模式command -v nginx /dev/null 21 log INFO nginx 已存在跳过安装 || apt-get install -y nginx这个模式可以扩展到所有安装函数加上前面 precheck 的端口检测脚本跑两遍、三遍结果都是一致的。幂等性做好的脚本才是真正可以放心交给新人的脚本。5.4 非交互安装被交互确认卡住现象脚本执行到apt-get install就停住不动SSH 会话里一直显示Do you want to continue? [Y/n]。原因安装命令没有带-y而脚本是无人值守跑的没人按回车。解决包管理命令全部加-y同时设置环境变量export DEBIAN_FRONTENDnoninteractive apt-get install -y nginxDEBIAN_FRONTENDnoninteractive会让 apt 跳过所有交互提示使用默认选项。yum 系则直接yum install -y。这两行写在所有安装函数开头能省掉大量半夜被唤醒的尴尬。5.5 日志缺失出问题不知道脚本执行到哪一步现象脚本半夜跑失败第二天看终端已经断了没有任何输出留存不知道是卡在源修复还是卡在安装环节。原因日志只打到 stdout没有落盘SSH 断开日志就丢了。解决脚本里统一封装日志函数输出同时写文件和终端文件名带时间戳LOG_DIR${SCRIPT_DIR}/logs LOG_FILE${LOG_DIR}/one-click-$(date %Y%m%d-%H%M%S).log log() { local level$1 shift echo [$(date %F %T)] [${level}] $* | tee -a ${LOG_FILE} }在关键步骤前后各打一条log INFO 开始修复软件源和log INFO 软件源修复完成出问题时看日志最后一条就知道卡在哪。加日志的成本很低排查时的收益却极高。linux 运维里没有日志的脚本就是黑匣子谁都不愿意碰。6. 进阶安全校验、幂等验证与上线前验收脚本能跑通只是第一步能安全地反复跑才是真正能投产的标准。我现在每写一个模块都会加一个校验步骤如果是远程下载的安装包下载后先做 SHA256 校验和 conf 目录里记录的哈希值比对通过后才执行安装。现在的网络环境里供应链攻击越来越多管道直接执行远程脚本的操作能避免就避免。幂等验证我有一个固定流程找一台干净的虚拟机跑两遍同一脚本第一遍看安装是否成功第二遍看是否无操作或只做了跳过。两遍结束后对比服务的状态和关键文件内容如果没有任何差异这个脚本才算过了我这一关。上线前验收我按下面这个清单逐项打勾验证项验证方法预期结果从零安装最小化安装的操作系统上跑脚本服务全部 active端口监听正常修复能力手动破坏软件源再跑修复模块apt update / yum makecache 返回 0幂等性连续执行两遍安装脚本第二遍无报错无重复安装日志完备检查 logs 目录生成的文件关键步骤有 INFO 记录无 ERROR重启验证执行 reboot 后再次检查服务全部服务自动启动最后一步重启验证不能省因为systemctl enable配置是否正确只有重启后才见真章。我现在接手任何一台新服务器都会先跑预检看系统状态再决定是修复还是安装修复前先备份、安装前先查端口这件事已经成了习惯比脚本本身更值钱。等你把上面这套框架跑通几台机器你会发现所谓一键脚本核心就是把自己的操作习惯固化下来再让机器替你把每一步都做对。希望帮到你。本文还有配套的精品资源点击获取