ARTICLE · 1013862
公共电脑维护利器:银河麒麟桌面系统「开机自动还原」部署方案(rsync 版)

适用:银河麒麟桌面操作系统 V10(含 SP1、2207/2303 等版本,x86_64 与 ARM64 飞腾/鲲鹏版通用)方案:基于 rsync 快照还原的一键脚本
kylin-reset-rsync.sh场景:教室、图书馆、政务大厅、单位公共机等「多人共用、用完即净」的环境文章结尾附脚本内容,也可关注公众号发送“开机自动还原”获取下载链接
一、为什么公共电脑需要"开机自动还原"
公共电脑被多人随意使用时,常见的"被弄脏"方式包括:
桌面、下载、文档目录堆满文件,删不掉、清不完;
乱装软件、改乱系统设置、改坏桌面环境;
误删系统文件导致无法开机。
传统人工清理费时费力且容易漏。更彻底的做法是开机自动还原:每次开机都把系统恢复到管理员预设的"出厂状态",用户的一切改动在重启后自动消失。本方案完全基于 rsync,开源、离线可用、不依赖网络,非常适合内网与信创环境。
二、方案原理
kylin-reset-rsync.sh 的核心思路:
首次部署时,把当前干净的
/home打成一份模板快照,存放到指定的分区目录;之后每次开机(在用户登录之前),系统自动用 rsync 把
/home覆盖还原为模板状态,用户改动全部清除;系统层(/usr、/opt、/etc)保持真实可写:管理员可随时升级应用、安装软件、修改配置,且不会被还原。
为什么要"只还原 /home、不动系统层"?
普通用户没有 root 权限,本就改不了系统层,真正会被弄脏的就是家目录、桌面;
系统层保持可写,意味着管理员升级应用、装软件、改配置零阻碍,无需任何额外操作;
只还原家目录,还原快、占用小、风险低。
这套架构尤其适合需要经常给公共机升级软件、调整配置的单位——还原不干扰维护,维护不破坏还原。
三、部署前检查清单(务必逐项完成)
1. 查看磁盘与分区布局
lsblk重点关注:
系统根分区在哪(MOUNTPOINT 为
/的那行);是否有一块独立的数据盘或未使用的空闲分区(用于存放模板快照);
是否有出厂自带的 timeshift 分区(挂载点形如
/run/timeshift/backup)。
2. 是否需要提前安装 timeshift?(常见疑问,先回答)
不需要。
本方案完全不依赖 timeshift,rsync 是系统自带命令;
麒麟部分版本(如 V10 2207 出厂镜像)自带 timeshift 及其备份分区(通常是一个几百 G、挂载在
/run/timeshift/backup的分区)。如果你发现机器里有这样的分区,可以直接复用它的空间存放模板快照,不用额外腾地方;如果没有 timeshift 分区,也没关系:方案只需一个可写目录,可以放在任何有权限的挂载点上,或用一块空闲分区单独挂载。
一句话:有 timeshift 分区就顺手复用,没有也不用装,方案照常可用。
3. 准备模板存放空间
在磁盘上确认一个"有剩余空间、且不是系统根分区"的位置。推荐优先级:
出厂 timeshift 备份分区(如
/run/timeshift/backup,空间大);独立数据盘或未使用分区;
任何可写的大容量挂载点。
模板快照大小约等于家目录的实际占用,建议存放位置至少预留 10~20G 以上余量。
没有 timeshift 分区怎么办? 本方案只需一个可写目录,按机器情况三选一即可:
/data) | --dev=/dev/mapper/卷组--逻辑卷 --mp=/data | |
--dir=/var/lib/reset-template | ||
--dev=/dev/sdX --mp=/挂载点 |
三者都不需要提前安装 timeshift。内置数据分区(如已挂载的
/data)最省心:不占系统盘、开机自动就位;移动硬盘存在"忘插导致还原失效"的风险,仅作备选。
四、部署步骤
第 1 步:准备脚本并赋权
将脚本 kylin-reset-rsync.sh 拷贝到目标机(U 盘或内网共享均可):
chmod +x kylin-reset-rsync.sh第 2 步:一键部署
# 参数说明: --dev 指定模板存放分区, --mp 指定该分区挂载点sudo bash kylin-reset-rsync.sh deploy --dev=/dev/sdX --mp=/挂载点示例:机器上 timeshift 备份分区为
/dev/sda5、挂载在/run/timeshift/backup,则执行sudo bash kylin-reset-rsync.sh deploy --dev=/dev/sda5(
--mp默认即为/run/timeshift/backup,无需重复指定)
例如:我的情况是有数据分区,但是没有安装timeshift,所以我是参照“3.准备模板存放空间”中第一行执行的命令。由于该脚本是默认情况是安装了timeshift,且指定了备份分区/dev/nvme0n1p5 ,所以每次执行命令的后面都有指定备份分区和挂载位置,具体情况可看图片标红处。

首次部署脚本会自动完成:
校验分区存在且非系统根分区(防呆);
挂载目标分区;
将当前
/home打成模板快照;生成开机还原服务并设置为开机自启;
备份原 fstab / grub 配置。
第 3 步:重启生效
sudo reboot重启后,每次开机都会自动把 /home 恢复为模板状态,用户改动全部还原。
日常维护(管理员)
# 升级应用 / 安装软件 / 修改配置 —— 直接做, 不受还原影响sudo apt update && sudo apt upgrade# 配置满意后, 重新固化模板(下次开机按新模板还原)sudo bash kylin-reset-rsync.sh update-template# 查看部署状态sudo bash kylin-reset-rsync.sh status# 需要彻底暂停还原做深度维护时(重启后生效)sudo bash kylin-reset-rsync.sh maintenance on# 维护完成恢复还原sudo bash kylin-reset-rsync.sh maintenance off# 卸载还原sudo bash kylin-reset-rsync.sh uninstall五、注意事项(重要,请逐条阅读)
部署前备份重要数据:脚本会自动备份 fstab、grub 配置,但请确保系统与用户关键数据已有备份再操作。
模板分区严禁选系统根分区:模板快照如果放在系统分区自身,还原时会被自己覆盖,务必用独立分区或独立挂载点。
格式化是危险操作:若对数据分区执行
mkfs.ext4格式化,会清空该分区全部数据。执行前务必 lsblk核对设备名,确认不是系统盘再操作。还原范围与系统升级:本方案默认只还原
/home。系统层升级、装软件、改配置会保留(不会被还原),这是设计意图;如确需把 /etc、/opt等系统配置也纳入还原范围,可调整脚本中的还原范围配置,但建议谨慎——避免还原把管理员配置也一并清掉。用户数据重启即失:部署后,任何用户在
/home下保存的文件、桌面文件、浏览器下载等,重启后都会消失。务必在公共机旁贴提示,并考虑提供一个公共共享目录(不参与还原)供用户临时保存文件,或引导用户用 U 盘保存。root 权限管控:还原方案只在管理员有 root 权限的前提下生效。请确认普通用户没有 sudo 权限,否则用户可自行关闭还原服务、绕过管控。建议同时开启麒麟系统的账户权限管理。
fstab / grub 自动备份:脚本会在修改前备份
/etc/fstab与 /etc/default/grub(带时间戳)。若机器无法开机,可在应急环境(live CD 或单用户模式)用备份文件恢复。数据盘故障兜底:脚本对模板分区挂载使用了
nofail选项,即使还原分区损坏或缺失,系统也能正常开机,不会卡在启动阶段。维护模式不要忘记关闭:
maintenance on只对下一次启动生效;维护完成后务必执行 maintenance off,否则还原将一直暂停。模板更新要及时:管理员升级系统、装新软件后,记得执行
update-template重新固化模板,否则下次开机仍会还原到旧模板,升级等于白做。ARM 与 x86 通用性:本方案命令在两个架构的麒麟桌面系统上完全一致,无需区分。
空间预留:模板快照大小约等于家目录实际占用,请确保模板分区有足够剩余空间。
六、常见问题排查
lsblk 核对后用 --dev= 指定真实分区 | ||
systemctl is-enabled kylin-reset.service 确认,未启用则重新部署 | ||
update-template | ||
/data)、根分区专用目录(--dir=)或常插移动硬盘,见第三节 |
七、总结
开机自动还原:基于 rsync 快照,兼容性好、单硬盘可用、不依赖网络;
不需要提前安装 timeshift:有现成 timeshift 分区顺手复用,没有也不影响;
只还原 /home、系统区真实可写:兼顾"防脏"与"管理员自由维护",升级装软件零阻碍;
务必先 lsblk 核对磁盘、备份数据、管好普通用户权限、贴好"数据重启即失"提示。
把公共电脑交给"开机自动还原",一劳永逸,让管理员从"天天清理"里解放出来。
附:完整一键部署脚本(kylin-reset-rsync.sh)
#!/usr/bin/env bash#===============================================================================# 文件名 : kylin-reset-rsync.sh# 适用 : 银河麒麟 Kylin V10 桌面 (ARM/x86 均适用)# 针对单硬盘 / 无独立数据盘 / 5.4 内核(无 overlayfs 模块)机器设计# 功能 : 公共电脑「开机自动还原」— rsync 快照还原版## 原理 : 首次部署把 /home 打成模板快照, 存放于指定分区目录(默认复用# nvme0n1p5 的 /run/timeshift/backup 分区, 空间大且不占用系统盘);# 开机时(登录前)自动把 /home 恢复为模板状态, 秒~分钟级。# 系统区(/usr /opt /etc)保持真实可写: 管理员升级应用/装软件/改配置不受影响,# 这些操作属于期望保留项, 不会被还原。## 维护流程: 管理员正常升级/配置 → 执行 update-template 重新打快照 → 下次开机生效## 用法示例:# sudo bash kylin-reset-rsync.sh deploy # 一键部署# sudo bash kylin-reset-rsync.sh deploy --dev=/dev/nvme0n1p5 --mp=/run/timeshift/backup# sudo bash kylin-reset-rsync.sh update-template # 重新固化模板(管理员维护后)# sudo bash kylin-reset-rsync.sh maintenance on # 暂停还原(重启生效)# sudo bash kylin-reset-rsync.sh maintenance off # 恢复还原# sudo bash kylin-reset-rsync.sh status # 查看状态# sudo bash kylin-reset-rsync.sh uninstall # 卸载还原#===============================================================================set -uSCRIPT_NAME="$(basename "$0")"#----------------------- 可配置项(支持 --dev / --mp / --dir 覆盖) --------------TPL_DEV="${TPL_DEV:-/dev/nvme0n1p5}" # 模板快照存放分区TPL_MP="${TPL_MP:-/run/timeshift/backup}" # 该分区挂载点TPL_DIR="${TPL_DIR:-$TPL_MP/reset-template}" # 模板快照目录# 开机要还原的目录(用空格分隔, 相对根路径; 默认仅 /home)# 如需连系统配置一起还原可改为: "home etc opt usr/local root"RESTORE_LIST="${RESTORE_LIST:-home}"RUNNER="/usr/local/sbin/kylin-reset-run.sh"SVC="/etc/systemd/system/kylin-reset.service"GRUB_CFG="/etc/default/grub"#----------------------- 工具函数 ----------------------------------------------red(){ printf '\033[31m%s\033[0m\n' "$*"; }grn(){ printf '\033[33m%s\033[0m\n' "$*"; }ylw(){ printf '\033[33m%s\033[0m\n' "$*"; }log(){ echo "[$(date '+%F %T')] [kylin-reset] $*"; }die(){ red "错误: $*"; exit 1; }require_root(){ [ "$(id -u)" -eq 0 ] || die "请用 root 运行: sudo bash $SCRIPT_NAME ..."; }check_os(){ local pretty="unknown" [ -r /etc/os-release ] && { . /etc/os-release; pretty="${PRETTY_NAME:-unknown}"; } case "$pretty" in *Kylin*|*kylin*|*银河麒麟*|*麒麟*) : ;; *) ylw "警告: 未检测到麒麟系统特征, 可能存在兼容风险 (当前: $pretty)" ;; esac grn "系统: $pretty | 内核: $(uname -r)"}ensure_mount(){ grep -q " $TPL_MP " /proc/mounts || { log "挂载 $TPL_DEV -> $TPL_MP" mkdir -p "$TPL_MP" mount "$TPL_DEV" "$TPL_MP" || die "挂载 $TPL_DEV 失败, 请核对分区/挂载点" }}update_grub_cfg(){ if command -v update-grub >/dev/null 2>&1; then update-grub; elif [ -f /boot/grub2/grub.cfg ]; then grub2-mkconfig -o /boot/grub2/grub.cfg; else grub-mkconfig -o /boot/grub/grub.cfg; fi}snapshot(){ local d sub tgt for d in $RESTORE_LIST; do sub="${d##*/}" mkdir -p "$TPL_DIR/$sub" log "打快照: /$d -> $TPL_DIR/$sub" rsync -a --delete "/$d/" "$TPL_DIR/$sub/" done}#----------------------- deploy ------------------------------------------------cmd_deploy(){ require_root check_os if systemctl is-enabled kylin-reset.service >/dev/null 2>&1 || [ -f "$RUNNER" ]; then ylw "检测到已部署过。更新模板请执行: bash $SCRIPT_NAME update-template; 重装请先 uninstall" exit 0 fi lsblk -o NAME "$TPL_DEV" >/dev/null 2>&1 || die "分区 $TPL_DEV 不存在, 请 lsblk 核对后用 --dev= 指定" ensure_mount log "首次部署: 制作 /home 模板快照..." snapshot grn "模板快照已生成: $TPL_DIR" # 生成开机还原执行脚本 cat > "$RUNNER" <<EOF#!/bin/sh# kylin-reset-run.sh (由部署脚本自动生成)if grep -qw maintenance=1 /proc/cmdline; then exit 0; fi # 维护模式: 跳过还原TPL_MP="$TPL_MP"; TPL_DIR="$TPL_DIR"; TPL_DEV="$TPL_DEV"grep -q " \$TPL_MP " /proc/mounts || { mkdir -p "\$TPL_MP"; mount "\$TPL_DEV" "\$TPL_MP" 2>/dev/null || exit 0; }for d in $RESTORE_LIST; do sub="\${d##*/}" [ -d "\$TPL_DIR/\$sub" ] && rsync -a --delete "\$TPL_DIR/\$sub/" "/\$d/"doneEOF chmod +x "$RUNNER" # 生成开机服务 cat > "$SVC" <<EOF[Unit]Description=Public pc auto restore (kylin-reset)DefaultDependencies=noAfter=local-fs.targetBefore=display-manager.service getty@tty1.service[Service]Type=oneshotExecStart=$RUNNERRemainAfterExit=yes[Install]WantedBy=multi-user.targetEOF systemctl daemon-reload systemctl enable kylin-reset.service >/dev/null 2>&1 grn "" grn "================ 部署完成 ================" grn " 开机自动还原: $RESTORE_LIST (默认 /home)" grn " 模板位置 : $TPL_DIR" grn " 系统区/应用升级不受影响, 可正常安装与配置" ylw " 下一步: sudo reboot 后 /home 即被开机还原管控" ylw " 维护后固化模板: sudo bash $SCRIPT_NAME update-template" grn "=========================================="}#----------------------- update-template ----------------------------------------cmd_update_template(){ require_root [ -f "$RUNNER" ] || die "尚未部署, 请先执行 deploy" ensure_mount ylw ">>> 将把当前 /home 状态固化为模板(即成为下次开机还原的目标) <<<" read -r -p "确认? 输入 yes 继续: " ans [ "$ans" = "yes" ] || die "已取消" snapshot grn "模板已更新, 下次开机生效。"}#----------------------- maintenance --------------------------------------------cmd_maintenance(){ require_root local mode="${1:-}" case "$mode" in on) [ -f "$GRUB_CFG" ] || die "未找到 $GRUB_CFG" cp "$GRUB_CFG" "$GRUB_CFG.bak.marvis-reset.$(date +%Y%m%d%H%M%S)" grep -qw maintenance=1 "$GRUB_CFG" \ || sed -i 's/GRUB_CMDLINE_LINUX="\([^"]*\)"/GRUB_CMDLINE_LINUX="\1 maintenance=1"/' "$GRUB_CFG" update_grub_cfg >/dev/null 2>&1 grn "已启用维护模式(重启后还原暂停)。维护完成: bash $SCRIPT_NAME maintenance off" ;; off) [ -f "$GRUB_CFG" ] || die "未找到 $GRUB_CFG" sed -i 's/ maintenance=1//g' "$GRUB_CFG" update_grub_cfg >/dev/null 2>&1 grn "已关闭维护模式, 重启后恢复自动还原。" ;; *) die "用法: $SCRIPT_NAME maintenance on|off" ;; esac}#----------------------- status ------------------------------------------------cmd_status(){ echo "================ 部署状态 ================" echo "模板分区 : $TPL_DEV -> $TPL_MP" lsblk -o NAME,SIZE,FSTYPE,MOUNTPOINT "$TPL_DEV" 2>/dev/null || echo " 未找到 $TPL_DEV" echo "模板目录 : $TPL_DIR" [ -d "$TPL_DIR/home" ] && du -sh "$TPL_DIR/home" 2>/dev/null || echo " 尚无模板快照" echo "还原服务 : $(systemctl is-enabled kylin-reset.service 2>/dev/null || echo 未启用)" grep -qw maintenance=1 /proc/cmdline && echo "运行模式 : [维护模式] 还原暂停" || echo "运行模式 : [公共模式] 开机自动还原" echo "还原范围 : $RESTORE_LIST" echo "=========================================="}#----------------------- uninstall ----------------------------------------------cmd_uninstall(){ require_root ylw ">>> 即将卸载开机还原 <<<" read -r -p "确认? 输入 yes 继续: " ans [ "$ans" = "yes" ] || die "已取消" systemctl disable kylin-reset.service 2>/dev/null || true rm -f "$SVC" "$RUNNER" systemctl daemon-reload grn "已卸载还原。模板快照未删除: $TPL_DIR (如需清理: sudo rm -rf $TPL_DIR)"}#----------------------- main ---------------------------------------------------main(){ local cmd="" i=0 local opt_args=() positional=() # 分离子命令 / 选项 / 位置参数(选项可写在子命令前或后) for a in "$@"; do if [ -z "$cmd" ]; then case "$a" in --*) opt_args+=("$a") ;; *) cmd="$a" ;; esac else case "$a" in --*) opt_args+=("$a") ;; *) positional+=("$a") ;; esac fi done # 解析选项(支持 --dev=x 与 --dev x 两种写法) while [ $i -lt ${#opt_args[@]} ]; do a="${opt_args[$i]}" case "$a" in --dev=*) TPL_DEV="${a#--dev=}" ;; --mp=*) TPL_MP="${a#--mp=}" ;; --dir=*) TPL_DIR="${a#--dir=}" ;; --dev) [ $((i+1)) -lt ${#opt_args[@]} ] && { TPL_DEV="${opt_args[$((i+1))]}"; i=$((i+1)); } ;; --mp) [ $((i+1)) -lt ${#opt_args[@]} ] && { TPL_MP="${opt_args[$((i+1))]}"; i=$((i+1)); } ;; --dir) [ $((i+1)) -lt ${#opt_args[@]} ] && { TPL_DIR="${opt_args[$((i+1))]}"; i=$((i+1)); } ;; esac i=$((i+1)) done case "$cmd" in deploy|install) cmd_deploy ;; update-template|update) cmd_update_template ;; maintenance) cmd_maintenance "${positional[0]:-}" ;; status) cmd_status ;; uninstall|remove) cmd_uninstall ;; help|-h|--help|"") usage_text ;; *) red "未知命令: $cmd"; exit 1 ;; esac}usage_text(){ cat <<'EOF'用法: sudo bash kylin-reset-rsync.sh <命令> [选项]命令: deploy [--dev=分区] [--mp=挂载点] [--dir=目录] 一键部署开机还原 update-template 重新固化模板(管理员维护后) maintenance on|off 切换维护模式 status 查看状态 uninstall 卸载还原选项(可写在子命令前或后): --dev=/dev/xxx 模板分区 (默认 /dev/nvme0n1p5) --mp=/挂载点 该分区挂载点 (默认 /run/timeshift/backup) --dir=/目录 模板目录 (默认 <挂载点>/reset-template)示例: sudo bash kylin-reset-rsync.sh deploy --dev=/dev/sda5 sudo bash kylin-reset-rsync.sh --dev=/dev/sda5 deployEOF}main "$@"