AI 摘要

在这个教程中,我们将探索如何在 Steam Deck 上将 Steam OS 安装到 TF 卡,从而实现双系统的完美搭配。大多数在线指导仅关注将 Windows 系统安装至 TF 卡,而我们将打破常规,优化性能,避免常见错误,并提供最新命令和工具,确保安装过程顺利无痛。准备好改变你的 Steam Deck 使用体验了吗?快来发现全新的可能性!

目前网上的 Steam Deck 安装双系统教程大部分都是将 Windows 系统安装到 TF 卡中。因为日常使用 Windows 系统多一点,并且需要运行一些大型软件,TF 卡的速度严重影响到了软件的使用,故想将 Windows 系统安装到固态中,将 Steam OS 安装到 TF 卡中。 在将 Steam OS 安装到 TF 卡的过程中,发现目前教程较少且已经过时,最新的 Steam OS 恢复映像中将系统安装到固态时使用的部分命令(NVMe)与 TF 卡不兼容,故编写此教程。

在 TF 卡上安装 Steam OS 时 Deck 内部的固态不能存在 Steam OS 系统!!! 安装中用到的代码在文末也一并给出。

一、准备工作

  1. TF 卡

  2. TF 读卡器

  3. 键盘、鼠标

  4. Type-c 扩展坞

  5. USB 3.0 以上 U 盘

  6. balenaEtcher 映像刷写工具 https://etcher.balena.io/

  7. Steam OS 恢复映像刷写命令文件夹(Deck_Repair 文件夹) https://gitee.com/kafuucoori/deck_repair

  8. Windows 恢复映像(在 U盘上 创建 Windows 11 安装媒体 https://www.microsoft.com/zh-cn/software-download/windows11

  9. Deck Windows 驱动(选择 符合自己版本 的驱动下载) https://help.steampowered.com/zh-cn/faqs/view/6121-ECCD-D643-BAA8

  10. Steam OS 恢复映像(找到 重映像 / 安装 SteamOS 处下载恢复映像) https://help.steampowered.com/zh-cn/faqs/view/1B71-EDF2-EB6D-2BB3

二、安装 Windows 系统

  1. 插入 U盘 以管理员身份运行安装媒体创建工具

  2. 选择 为另一台 PC 创建安装媒体,然后选择下一步。

  3. 选择适用于 Windows 11 的语言、版本和体系结构(64 位)

  4. 选择媒体 USB 闪存驱动器:U盘 的容量至少 8GB ,其中的 所有内容都会被删除

  5. 关闭 Steam Deck,按住降低音量键并按下电源键,在打开的菜单中选择自己的 U盘

  6. 在安装 Windows 页上,选择语言、时间和键盘首选项,然后选择下一步,安装 Windows

  7. 安装完成后进入系统,将刚才下载的 Deck Windows 驱动解压后依次安装

三、安装 Steam OS 系统

  1. 解压 Steam OS 恢复映像(最好用 WinRAR,我用 7-zip 解压出来的有问题)

  2. 管理员身份运行 balenaEtcher,从文件烧录选择刚才解压的 Steam OS 恢复映像

  3. 选择目标磁盘时选择自己的 U盘,点击现在烧录,等待映像烧录完成后插入 Deck

  4. 格式化 TF 卡,将 Deck_Repair 代码文件夹拷贝到 TF 卡中,将 TF 卡插入 Deck 的卡槽

  5. 关闭 Steam Deck,按住降低音量键并按下电源键,在打开的菜单中选择自己的 U盘 进入桌面环境

  6. 进入左下角的文件管理器,进入 TF 卡的目录,将 Deck_Repair 文件夹拷贝到桌面

  7. 打开 Deck_Repair 文件夹,在当前目录下的空白处右键打开终端(Open in Terminal)

  8. 运行命令 sudo ./repair_device_sdcard.sh all ,在跳出的对话框中选择 Proceed

  9. 等待安装完成后会跳出是否重启的弹窗,选择不重启,使用桌面左下角的关机选项直接关机

  10. 关闭 Steam Deck 后,按住降低音量键并按下电源键,在打开的菜单中选择 TF 卡(Steam OS)

  11. 进入系统后会进入安装界面,在连接 WiFi 步骤后会进入系统更新界面,此时可能出现三种情况

    • 进度条成功走完,没有出现任何错误,那么恭喜你安装完成

    • 进度条离走完还有很远,显示更新错误——网络问题,更换网络或使用加速器

    • 进度条离走完只差一点点,该情况有两种解决方案,第二种的可行性未验证

      • 方案一:关机,从卡槽中拔出 TF 卡,将其插入读卡器后用 Deck 从读卡器中打开系统,继续刚才的安装,安装完成成功进入 Steam 登录界面后即可关机将 TF 卡插入卡槽中正常使用 音量-键和电源键 启动 OS

      • 方案二:关闭 Steam Deck,按住降低音量键并按下电源键,在打开的菜单中选择自己刚才用来安装系统的 U盘,进入桌面上拷贝的 Deck 文件夹,在当前目录下的空白处右键打开终端(Open in Terminal),运行命令 sudo ./repair_install_to_sdcard.sh,运行完成后从 TF 卡重启系统,继续之前的安装。(该方案来源于 https://github.com/ryanrudolfoba/SteamOS-microSD )具体细节请自行查看

四、后续工作

五、安装代码

请严格按照文件名创建文件,repair_install_to_sdcard 和 repair_install_sdcard 都需要创建!如无法运行,先执行命令 sudo chmod +x *.sh

  • repair_device_sdcard.sh
#!/bin/bash
# -*- mode: sh; indent-tabs-mode: nil; sh-basic-offset: 2; -*-
# vim: et sts=2 sw=2
#
# 在SD卡上创建、修复或修改SteamOS安装的函数集合。
#

set -eu

die() { echo >&2 "!! $*"; exit 1; }
readvar() { IFS= read -r -d '' "$1" || true; }

DISK=/dev/mmcblk0
DISK_SUFFIX=p
DOPARTVERIFY=1

# 如果此目录存在,使用此目录中的jupiter-biosupdate二进制文件
# 并在调用时将JUPITER_BIOS_DIR设置为此目录。
# 用于包含比基础镜像更新的bios载荷。
VENDORED_BIOS_UPDATE=/home/deck/jupiter-bios
# 如果此目录存在,使用此目录中的jupiter-controller-update二进制文件,
# 并在调用时将JUPITER_CONTROLLER_UPDATE_FIRMWARE_DIR设置为此目录。
# 用于包含比基础镜像更新的控制器载荷。
VENDORED_CONTROLLER_UPDATE=/home/deck/jupiter-controller-fw

# 分区表,sfdisk格式,%%DISKPART%%将被填入
PART_SIZE_ESP="256"
PART_SIZE_EFI="64"
PART_SIZE_ROOT="5120" # 这应该与输入磁盘构建的大小匹配
PART_SIZE_VAR="256"
PART_SIZE_HOME="100" 

# 总大小 + 开始/结束处各1MiB填充用于GPT结构。
DISK_SIZE=$(( 2 + PART_SIZE_HOME + PART_SIZE_ESP + 2 * ( PART_SIZE_EFI + PART_SIZE_ROOT + PART_SIZE_VAR ) ))
# 对齐:使用MiB等常规大小且没有明确的起始偏移点会导致sfdisk默认对齐到MiB边界

TARGET_SECTOR_SIZE=512 # 传递给 `losetup` 进行模拟,影响sfdisk最终写入的扇区偏移。
readvar PARTITION_TABLE <<END_PARTITION_TABLE
  label: gpt
  %%DISKPART%%1: name="esp",      size=         ${PART_SIZE_ESP}MiB,  type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B
  %%DISKPART%%2: name="efi-A",    size=         ${PART_SIZE_EFI}MiB,  type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7
  %%DISKPART%%3: name="efi-B",    size=         ${PART_SIZE_EFI}MiB,  type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7
  %%DISKPART%%4: name="rootfs-A", size=         ${PART_SIZE_ROOT}MiB, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709
  %%DISKPART%%5: name="rootfs-B", size=         ${PART_SIZE_ROOT}MiB, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709
  %%DISKPART%%6: name="var-A",    size=         ${PART_SIZE_VAR}MiB,  type=4D21B016-B534-45C2-A9FB-5C16E091FD2D
  %%DISKPART%%7: name="var-B",    size=         ${PART_SIZE_VAR}MiB,  type=4D21B016-B534-45C2-A9FB-5C16E091FD2D
  %%DISKPART%%8: name="home",     size=         ${PART_SIZE_HOME}MiB, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915
END_PARTITION_TABLE

# 理想目标设备上的分区号
FS_ESP=1
FS_EFI_A=2
FS_EFI_B=3
FS_ROOT_A=4
FS_ROOT_B=5
FS_VAR_A=6
FS_VAR_B=7
FS_HOME=8

diskpart() { echo "$DISK$DISK_SUFFIX$1"; }

##
## 实用工具颜色等
##

err() {
  echo >&2
  eerr "发生镜像错误,请查看上方并重新启动进程。"
  sleep infinity
}
trap err ERR

_sh_c_colors=0
[[ -n $TERM && -t 1 && ${TERM,,} != dumb ]] && _sh_c_colors="$(tput colors 2>/dev/null || echo 0)"
sh_c() { [[ $_sh_c_colors -le 0 ]] || ( IFS=\; && echo -n $'\e['"${*:-0}m"; ); }

sh_quote() { echo "${@@Q}"; }
estat()    { echo >&2 "$(sh_c 32 1)::$(sh_c) $*"; }
emsg()     { echo >&2 "$(sh_c 34 1)::$(sh_c) $*"; }
ewarn()    { echo >&2 "$(sh_c 33 1);;$(sh_c) $*"; }
einfo()    { echo >&2 "$(sh_c 30 1)::$(sh_c) $*"; }
eerr()     { echo >&2 "$(sh_c 31 1)!!$(sh_c) $*"; }
die() { local msg="$*"; [[ -n $msg ]] || msg="脚本已终止"; eerr "$msg"; exit 1; }
showcmd() { showcmd_unquoted "${@@Q}"; }
showcmd_unquoted() { echo >&2 "$(sh_c 30 1)+$(sh_c) $*"; }
cmd() { showcmd "$@"; "$@"; }

# 格式化辅助函数
fmt_ext4()  { [[ $# -eq 2 && -n $1 && -n $2 ]] || die; cmd sudo mkfs.ext4 -F -L "$1" "$2"; }
fmt_fat32() { [[ $# -eq 2 && -n $1 && -n $2 ]] || die; cmd sudo mkfs.vfat -n"$1" "$2"; }

##
## 提示函数
##

# 给用户一个选择:继续或取消(退出此脚本)
#  $1 标题
#  $2 文本
#
prompt_step()
{
  title="$1"
  msg="$2"
  unconditional="${3-}"
  if [[ ! ${unconditional-} && ${NOPROMPT:-} ]]; then
    echo -e "$msg"
    return 0
  fi
  zenity --title "$title" --question --ok-label "继续" --cancel-label "取消" --no-wrap --text "$msg"
  [[ $? = 0 ]] || exit 1
}

prompt_reboot()
{
  local msg=$1
  local mode="reboot"
  [[ ${POWEROFF:-} ]] && mode="shutdown"

  prompt_step "操作成功" "${msg}\n\n选择继续立即$mode,或选择取消停留在修复镜像中。" "${REBOOTPROMPT:-}"
  [[ $? = 0 ]] || exit 1
  if [[ ${POWEROFF:-} ]]; then
    cmd systemctl poweroff
  else
    cmd systemctl reboot
  fi
}

##
## 修复函数
##

# 验证目标磁盘上的分区 - 至少确保类型和分区标签与期望的匹配。
#   $1 设备
#   $2 期望的类型
#   $3 期望的分区标签
#
verifypart()
{
  [[ $DOPARTVERIFY = 1 ]] || return 0
  TYPE="$(blkid -o value -s TYPE "$1" )"
  PARTLABEL="$(blkid -o value -s PARTLABEL "$1" )"
  if [[ ! $TYPE = "$2" ]]; then
    eerr "设备 $1 的类型是 $TYPE 但期望 $2 - 无法继续。您可以尝试完全恢复。"
    sleep infinity ; exit 1
  fi

  if [[ ! $PARTLABEL = $3 ]] ; then 
    eerr "设备 $1 的标签是 $PARTLABEL 但期望 $3 - 无法继续。您可以尝试完全恢复。"
    sleep infinity ; exit 2
  fi
}

# 替换设备rootfs(btrfs版本)。调用前源必须被冻结。
#   $1 源设备
#   $2 目标设备
#
imageroot()
{
  local srcroot="$1"
  local newroot="$2"
  # 复制然后随机化目标UUID - 这里要小心!复制btrfs ID是有问题的
  cmd dd if="$srcroot" of="$newroot" bs=128M status=progress oflag=sync
  cmd btrfstune -f -u "$newroot"
  cmd btrfs check "$newroot"
}

# 在目标分区集中设置启动配置
#   $1 分区集名称
#
finalize_part()
{
  estat "正在完成安装分区 $1"
  cmd steamos-chroot --no-overlay --disk "$DISK" --partset "$1" -- mkdir /efi/SteamOS
  cmd steamos-chroot --no-overlay --disk "$DISK" --partset "$1" -- mkdir -p /esp/SteamOS/conf
  cmd steamos-chroot --no-overlay --disk "$DISK" --partset "$1" -- steamos-partsets /efi/SteamOS/partsets
  cmd steamos-chroot --no-overlay --disk "$DISK" --partset "$1" -- steamos-bootconf create --image "$1" --conf-dir /esp/SteamOS/conf --efi-dir /efi --set title "$1"
  cmd steamos-chroot --no-overlay --disk "$DISK" --partset "$1" -- grub-mkimage
  cmd steamos-chroot --no-overlay --disk "$DISK" --partset "$1" -- update-grub
}

##
## 主程序
##

onexit=()
exithandler() {
  for func in "${onexit[@]}"; do
    "$func"
  done
}
trap exithandler EXIT

# 检查目标磁盘是否存在
if [[ ! -e "$DISK" ]]; then
  eerr "$DISK 不存在 -- 未检测到SD卡?"
  sleep infinity
  exit 1
fi

# 重新安装全新的SteamOS副本。
#
repair_steps()
{
  if [[ $writePartitionTable = 1 ]]; then
    estat "写入已知分区表"
    echo "$PARTITION_TABLE" | sfdisk "$DISK"

  elif [[ $writeOS = 1 || $writeHome = 1 ]]; then

    # 验证分区设置
    verifypart "$(diskpart $FS_ESP)" vfat esp
    verifypart "$(diskpart $FS_EFI_A)" vfat efi-A
    verifypart "$(diskpart $FS_EFI_B)" vfat efi-B
    verifypart "$(diskpart $FS_VAR_A)" ext4 var-A
    verifypart "$(diskpart $FS_VAR_B)" ext4 var-B
    verifypart "$(diskpart $FS_HOME)" ext4 home
  fi

  # 清除var分区(用户数据)
  if [[ $writeOS = 1 || $writeHome = 1 ]]; then
    estat "创建var分区"
    fmt_ext4  var  "$(diskpart $FS_VAR_A)"
    fmt_ext4  var  "$(diskpart $FS_VAR_B)"
  fi

  # 创建启动分区
  if [[ $writeOS = 1 ]]; then
    # 设置ESP/EFI启动分区
    estat "创建启动分区"
    fmt_fat32 esp  "$(diskpart $FS_ESP)"
    fmt_fat32 efi  "$(diskpart $FS_EFI_A)"
    fmt_fat32 efi  "$(diskpart $FS_EFI_B)"
  fi

  if [[ $writeHome = 1 ]]; then
    estat "创建home分区..."
    cmd sudo mkfs.ext4 -F -O casefold -T huge -L home "$(diskpart $FS_HOME)"
    estat "移除home分区上的保留块..."
    tune2fs -m 0 "$(diskpart $FS_HOME)"
  fi

  # 如果更新操作系统,为下次重启准备 BIOS 更新。
  if [[ $writeOS = 1 ]]; then
    estat "如有必要,为下次启动暂存BIOS更新"
    biostool=/usr/bin/jupiter-biosupdate
    if [[ -n $VENDORED_BIOS_UPDATE && -d $VENDORED_BIOS_UPDATE ]]; then
      biostool="$VENDORED_BIOS_UPDATE"/jupiter-biosupdate
      export JUPITER_BIOS_DIR="$VENDORED_BIOS_UPDATE"
    fi

    fix_esp() {
      if [[ -n $mounted_esp ]]; then
        cmd umount -l /esp
        cmd umount -l /boot/efi
        mounted_esp=
      fi
    }
    onexit+=(fix_esp)
    einfo "在/esp /boot/efi上挂载新的ESP/EFI用于BIOS暂存"
    cmd mount "$(diskpart $FS_ESP)" /esp
    cmd mount "$(diskpart $FS_EFI_A)" /boot/efi
    mounted_esp=1

    if [[ ${FORCEBIOS:-} ]]; then
      "$biostool" --force || "$biostool"
    else
      "$biostool"
    fi

    fix_esp
  fi

  # 如果更新操作系统,执行控制器更新。OOBE 镜像不会在启动时自动更新控制器。
  if [[ $writeOS = 1 ]]; then
    estat "如有必要更新控制器固件"
    controller_tool="/usr/bin/jupiter-controller-update"

    if [[ -n $VENDORED_CONTROLLER_UPDATE && -d $VENDORED_CONTROLLER_UPDATE ]]; then
      controller_tool="$VENDORED_CONTROLLER_UPDATE"/jupiter-controller-update
      export JUPITER_CONTROLLER_UPDATE_FIRMWARE_DIR="$VENDORED_CONTROLLER_UPDATE"
    fi

    JUPITER_CONTROLLER_UPDATE_IN_OOBE=1 "$controller_tool"
  fi

  if [[ $writeOS = 1 ]]; then
    # 查找rootfs
    rootdevice="$(findmnt -n -o source / )"
    if [[ -z $rootdevice || ! -e $rootdevice ]]; then
      eerr "无法找到USB安装程序根目录 -- usb集线器问题?"
      sleep infinity
      exit 1
    fi

    estat "冻结rootfs"
    unfreeze() { fsfreeze -u /; }
    onexit+=(unfreeze)
    cmd fsfreeze -f /

    estat "为OS分区A制作镜像"
    imageroot "$rootdevice" "$(diskpart $FS_ROOT_A)"
  
    estat "为OS分区B制作镜像"
    imageroot "$rootdevice" "$(diskpart $FS_ROOT_B)"
  
    estat "完成启动配置"
    finalize_part A
    finalize_part B
    estat "完成EFI系统分区"
    cmd steamos-chroot --no-overlay --disk "$DISK" --partset A -- steamcl-install --flags restricted --force-extra-removable
  fi
}

# 进入设备上的主OS分区集
chroot_primary()
{
  partset=$( steamos-chroot --no-overlay --disk "$DISK" --partset "A" -- steamos-bootconf selected-image )

  estat "进入 $partset 分区集的chroot环境。"
  estat "您可以在这里进行任何需要的更改,完成后退出。"

  # FIXME etc overlay目录在全新安装时可能不存在

  cmd steamos-chroot --disk "$DISK" --partset "$partset"
}

# 打印目标的快速列表
help()
{
  readvar HELPMSG << EOD
此工具可用于重新安装或修复您在SD卡上的SteamOS安装

可能的目标:
    all : 永久销毁设备上的所有数据,并(重新)安装SteamOS。
    system : 修复/重新安装设备系统分区上的SteamOS,保留用户数据分区。
    home : 重新格式化设备的/home和/var分区,从设备中删除游戏和用户数据。
    chroot : chroot到主SteamOS分区集。
EOD
  emsg "$HELPMSG"
  if [[ "$EUID" -ne 0 ]]; then
    eerr "请以root身份运行。"
    exit 1
  fi
}

[[ "$EUID" -ne 0 ]] && help

writePartitionTable=0
writeOS=0
writeHome=0

case "${1-help}" in
all)
  prompt_step "擦除设备并安装SteamOS" "此操作将擦除并在此SD卡上(重新)安装SteamOS。\n这将永久销毁您设备上的所有数据。\n\n此操作无法撤销。\n\n只有当您希望擦除并重新安装此设备时,才选择继续。"
  writePartitionTable=1
  writeOS=1
  writeHome=1
  repair_steps
  prompt_reboot "重新制作镜像完成。"
  ;;
system)
  prompt_step "修复SteamOS" "此操作将修复设备上的SteamOS安装,同时尝试保留您的游戏和个人内容。\n系统自定义可能会丢失。\n\n选择继续在您的设备上重新安装SteamOS。"
  writeOS=1
  repair_steps
  prompt_reboot "SteamOS重新安装完成。"
  ;;
home)
  prompt_step "删除本地用户数据" "此操作将重新格式化您设备上的home分区。\n这将销毁下载的游戏和所有个人内容,包括系统配置。\n\n此操作无法撤销。\n\n选择继续重新格式化所有用户home分区。"
  writeHome=1
  repair_steps
  prompt_reboot "用户分区已重新格式化。"
  ;;
chroot)
  chroot_primary
  ;;
*)
  help
  ;;
esac
  • repair_install_to_sdcard.sh
#!/bin/bash

# 尝试卸载分区
sudo umount /dev/mmcblk0p8
sudo umount /dev/mmcblk0p7
sudo umount /dev/mmcblk0p6

# 删除自动挂载SD卡的udev规则
cmd echo "mount -o rw,remount / ; steamos-readonly disable; rm /usr/lib/udev/rules.d/99-sdcard-mount.rules" | steamos-chroot --disk /dev/mmcblk0 --partset A --

cmd echo "mount -o rw,remount / ; steamos-readonly disable; rm /usr/lib/udev/rules.d/99-sdcard-mount.rules" | steamos-chroot --disk /dev/mmcblk0 --partset B --

echo Start to insert script!

# 挂载home分区,如果不存在则创建deck的home目录和.repair目录
sudo mkdir -p /run/media/home
sudo mount /dev/mmcblk0p8 /run/media/home
sudo mkdir -p /run/media/home/deck/ &>/dev/null
sudo mkdir -p /run/media/home/deck/.repair &>/dev/null
sudo chown deck:deck /run/media/home/deck
sudo chown deck:deck /run/media/home/deck/.repair

# 将repair_install_sdcard.sh复制到.repair目录
FILE=/run/media/home/deck/.repair/repair_install_sdcard.sh
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
sudo cp "$SCRIPT_DIR/repair_install_sdcard.sh" $FILE
sudo chmod +x $FILE
sudo chown deck:deck $FILE

# 在SteamDeck操作系统的.profile文件中添加一行,以便在每次SteamDeck启动且deck用户登录时运行repair_install_sdcard.sh脚本
# 这确保用户无需手动运行repair_install_sdcard.sh脚本,特别是在全新安装过程中欢迎界面自动更新之前无法访问桌面控制台的情况下
cat >/run/media/home/deck/.profile <<EOF
~/.repair/repair_install_sdcard.sh
EOF

sudo umount /run/media/home

echo "Done!"
  • repair_install_sdcard.sh
#!/bin/bash

# 检查deck账户是否已设置sudo密码
if [ "$(passwd --status deck | tr -s " " | cut -d " " -f 2)" == "P" ]; then
	echo Sudo密码已经设置!
else
	echo 正在设置sudo密码 deck:deck
	# 自动输入密码"deck"两次来设置密码
	echo -e "deck\ndeck" | passwd deck &>/dev/null
	echo Sudo密码已设置!
fi

echo 正在删除SD卡自动挂载udev规则并卸载/run/media/var
# 禁用SteamOS只读模式以允许修改系统文件
echo -e "deck\n" | sudo -S steamos-readonly disable &>/dev/null
# 删除SD卡自动挂载的udev规则文件
sudo rm /usr/lib/udev/rules.d/99-sdcard-mount.rules &>/dev/null
# 重新加载udev规则并触发udev事件
sudo udevadm control --reload
sudo udevadm trigger
# 卸载可能存在的SD卡挂载点
sudo umount /run/media/var &>/dev/null
sudo umount /run/media/deck/var &>/dev/null
echo SD卡自动挂载udev规则已删除!

# 检查指定的systemd服务是否存在
# 参数:$1 - 服务名称(不包含.service后缀)
service_exists() {
	local n=$1
	# 列出所有服务单元,查找指定的服务
	if [[ $(systemctl list-units --all -t service --full --no-legend "$n.service" | sed 's/^\s*//g' | cut -f1 -d' ') == $n.service ]]; then
		return 0  # 服务存在
	else
		return 1  # 服务不存在
	fi
}

# 在用户配置文件中运行此脚本
# 确保.profile文件存在并具有可执行权限
if [ ! -f "/home/deck/.profile" ]; then
	touch /home/deck/.profile
	sudo chmod +x /home/deck/.profile
fi

# 创建repair目录用于存放相关脚本
mkdir -p ~/.repair &>/dev/null

# 检查并复制repair_install_sdcard.sh脚本
FILE=~/.repair/repair_install_sdcard.sh
if [ ! -f "$FILE" ]; then
	# 获取当前脚本所在目录
	SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
	# 复制repair_install_sdcard.sh到用户目录
	sudo cp "$SCRIPT_DIR/repair_install_sdcard.sh" $FILE
	sudo chmod +x $FILE
fi

# 在.profile中添加repair_install_sdcard.sh的执行命令(如果尚未添加)
if ! grep -qsF "repair/repair_install_sdcard.sh" "/home/deck/.profile"; then
	cat >>/home/deck/.profile <<EOF
~/.repair/repair_install_sdcard.sh
EOF
fi

# 创建SD卡最小化写入服务(如果不存在)
if ! service_exists sdcard_minimize_write; then
	# 创建最小化写入脚本
	# 该脚本的作用是减少对SD卡的写入操作,延长SD卡寿命
	cat >~/.repair/sdcard_minimize_write.sh <<EOF
#!/bin/bash
# 遍历所有mmcblk0p开头的挂载点(SD卡分区)
for mountpoint in \$(mount | grep mmcblk0p | tr -s " " | cut -d " " -f 3)
do 
	# 重新挂载分区,添加noatime标志以减少访问时间更新
	mount -o rw,remount,noatime \$mountpoint
	echo \$mountpoint 已使用noatime标志重新挂载。
done
# 关闭交换文件以减少对SD卡的写入
swapoff /home/swapfile &> /dev/null
# 卸载可能的var目录挂载
umount /run/media/deck/var &> /dev/null
exit 0
EOF

	sudo chmod +x ~/.repair/sdcard_minimize_write.sh
	echo 脚本已创建!

	# 创建systemd服务来运行上述脚本
	sudo rm /etc/systemd/system/sdcard_minimize_write.service
	cat <<EOF | sudo tee -a /etc/systemd/system/sdcard_minimize_write.service &>/dev/null
[Unit]
Description=最小化对SD卡的写入 - 设置noatime标志并禁用交换。

[Service]
User=root
ExecStart=/home/deck/.repair/sdcard_minimize_write.sh

[Install]
WantedBy=multi-user.target
EOF
	echo 服务已创建:sdcard_minimize_write
fi

# 创建microSD卡卸载服务(如果不存在)
if ! service_exists microsd-umount; then
	# 创建服务在启动时卸载/run/media/var
	# 这是为了在更新模式下重启时继续更新过程
	sudo rm /etc/systemd/system/microsd-umount.service
	cat <<EOF | sudo tee -a /etc/systemd/system/microsd-umount.service &>/dev/null
[Unit]
Description=在启动时尝试最多10次卸载/run/media/var。

[Service]
User=root
ExecStart=/bin/bash -c "for i in {0..9}; do if mountpoint -q -- /run/media/var; then umount /run/media/var; else sleep 1; fi; done"

[Install]
WantedBy=multi-user.target
EOF
	echo 服务已创建:microsd-umount

	# 启用并启动这两个服务
	sudo systemctl enable sdcard_minimize_write
	sudo systemctl enable microsd-umount
	sudo systemctl start sdcard_minimize_write
	sudo systemctl start microsd-umount
	echo 服务已启用并启动。
fi

# 禁用SteamOS只读模式
sudo steamos-readonly disable
exit 0
此作者没有提供个人介绍。
最后更新于 2025-06-24