Pony of Shadows

Queen of Truth & King of Practice


如何优雅地重新安装你的Arch Linux

日常使用

  1. 有序存放工作生活中使用的文件,并使用rsync等工具备份防止丢失
  2. 妥善配置系统,并使用脚本管理所有配置文件
  3. 对自己安装过的软件心中有数

在旧系统上备份

用脚本备份包和配置文件

下面的脚本作为参考

#/bin/bash

# update : a script to backup and restore my work environment on ArchLinux
## Please run this script as wheel user after basic install of Arch Linux.
# Principles
## 1. Take detailed notes in the script file
## 2. Always only do somesthing after judging that it has not been done yet.
## 3. exit if somesthing goes wrong.
set -e

## Config
bak_path="$HOME/all/local/share/backup-config"

# Backup or Recover?
case $1 in
  backup)
    option=""
    ;;
  recover)
    option="r"
    ;;
  *)
    echo "Please use option 'backup or 'recover'."
    exit 1
    ;;
esac

# Func
has_installed(){
  pacman -Qs | grep -E "^local/${1} " &> /dev/null
}
update_pkg(){
  if [[ -n $option ]]; then
    local pkg
    for pkg in "$@"; do
      has_installed "$pkg" || sudo pacman -S --noconfirm "$pkg"
    done
  fi
}
update_aur_pkg(){
  if [[ -n $option ]]; then
    local pkg
    for pkg in "$@"; do
      has_installed "$pkg" || yay -S --noconfirm "$pkg"
    done
  fi
}
update_config(){
  local config_file
  local config_basename
  local relative_dir
  for config_file in "$@"; do
    config_basename=$(basename "$config_file")
    if [[ "$config_file" == ~/* ]]; then
      # wheel-home
      relative_dir=$(dirname "${config_file/#$HOME/}")
      if [[ -n $option ]]; then
        # recover
        mkdir -p ~/$relative_dir
        rsync -rlpt $bak_path/wheel-home/$relative_dir/$config_basename ~/$relative_dir
      else
        # backup
        mkdir -p $bak_path/wheel-home/$relative_dir
        rsync -rlpt --delete "$config_file" $bak_path/wheel-home/$relative_dir/
      fi
    else
      # fs-root
      relative_dir=$(dirname "$config_file")   
      if [[ -n $option ]]; then
        # recover
        sudo mkdir -p $relative_dir
        sudo rsync -a --no-D $bak_path/fs-root/$relative_dir/$config_basename $relative_dir
        sudo chown -R root:root "$config_file"
      else
        # backup
        sudo mkdir -p $bak_path/fs-root/$relative_dir
        sudo rsync -a --no-D --delete "$config_file" $bak_path/fs-root/$relative_dir/
      fi
    fi
  done
}

# main
if id -nG "$USER" | grep -qw "wheel"; then
  # grub
  update_config /etc/default/grub
  [[ -n $option ]] && sudo grub-mkconfig -o /boot/grub/grub.cfg

  # Internet Connection
  ## DNS
  [[ -n $option ]] && sudo chattr -i /etc/resolv.conf
  update_config /etc/resolv.conf
  [[ -n $option ]] && sudo chattr +i /etc/resolv.conf
  ## wpa
  update_config /etc/wpa_supplicant
  wifi_interface=$(ip link show | awk -F: '/wl/ {print $2}' | tr -d ' ')
  if [[ -n $option ]]; then
    sudo mkdir -p "/etc/systemd/system/"
    sudo rm -f /etc/systemd/system/wpa_supplicant*
    sudo cp ${bak_path}/fs-root/etc/systemd/system/wpa_supplicant@*.service "/etc/systemd/system/wpa_supplicant@${wifi_interface}.service"
    sudo systemctl enable --now "wpa_supplicant@${wifi_interface}.service"
  else
    sudo mkdir -p "${bak_path}/fs-root/etc/systemd/system/"
    sudo rm -f ${bak_path}/fs-root/etc/systemd/system/wpa_supplicant*
    sudo cp "/etc/systemd/system/wpa_supplicant@${wifi_interface}.service" "${bak_path}/fs-root/etc/systemd/system/"
  fi
  ## dhcpcd
  [[ -n $option ]] && sudo systemctl enable --now dhcpcd
  echo "[Done] Internet Connection"
  
  # Time
  ## time zone
  [[ -n $option ]] && sudo ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
  ## RTC time
  [[ -n $option ]] && sudo timedatectl set-ntp on
  [[ -n $option ]] && sudo timedatectl set-local-rtc 0 
  ## write to device time
  [[ -n $option ]] && sudo hwclock --systohc
  echo "[Done] Time"
  
  # Pacman, AUR, git, pip
  update_config /etc/pacman.conf
  update_config /etc/pacman.d/mirrorlist
  update_config /etc/pacman.d/hooks/update_snapshot.hook
  [[ -n $option ]] && sudo pacman -Syu --noconfirm
  update_pkg git git-lfs openssh
  update_config ~/.gitconfig
  update_config ~/.ssh # ssh for github
  [[ -n $option ]] && git lfs install
  update_config ~/.pip/pip.conf
  ## yay, see: https://wiki.archlinuxcn.org/wiki/Yay
  if [[ -n $option ]] && ! has_installed "yay-bin" ; then
    cd ~/local/lib/yay-bin/
    git pull origin master
    makepkg -si --noconfirm
  fi
  echo "[Done] Pacman, AUR, Git, Pip"

  # language & Font
  ## lang
  update_config /etc/locale.gen
  [[ -n $option ]] && sudo locale-gen
  update_config /etc/locale.conf
  ## font
  update_pkg wqy-microhei noto-fonts-cjk noto-fonts-emoji noto-fonts-extra
  update_config /etc/fonts/conf.d/64-language-selector-prefer.conf
  echo "[Done] Font"

  # dir
  update_config ~/.config/user-dirs.conf
  update_config ~/.config/user-dirs.dirs
  echo "[Done] Dir"

  # Shell
  ## bash
  update_pkg bash-completion fzf powerline powerline-fonts
  update_config ~/.bash
  update_config ~/.bash_profile
  update_config ~/.bashrc
  ## nvim
  update_pkg neovim
  update_pkg ccls jedi-language-server rust-analyzer texlab # language servers
  update_aur_pkg cmake-language-server fortls
  update_config ~/.config/nvim # Please wait at first start
......
  # develop
  update_pkg jdk17-openjdk jdk21-openjdk # java for minecraft
  update_pkg rust
  update_pkg gdb
  update_pkg inetutils # MATHEMATICA requires
  [[ -n $option ]] && sudo systemctl enable --now avahi-daemon.service # for MATHEMATICA
  echo "[Done] develop"

  # reboot
  [[ -n $option ]] && echo "Alright. Please reboot..." 

else
  echo "Please run this script as user in wheel group"
  exit 1
fi

echo "OK"

备份所有必要文件到备用硬盘

我所有有用的文件储存在~/all/目录下,并通过自己写的一个名叫poshd的bash脚本管理。

像这样备份all//mnt/:

po -b /mnt

新系统的基础安装

参考之前的文章

用live系统启动

制作USB闪存安装介质

  1. Download页面找到自己所在国家的镜像站下载最新的ISO(光盘镜像文件),对于中国,可以是中科大镜像源。比如下面这两个文件,都要下载下来, 放在同一个目录待验证
archlinux-2024.06.01-x86_64.iso                    01-Jun-2024 17:09          1168146432
archlinux-2024.06.01-x86_64.iso.sig                01-Jun-2024 17:09                 141

前者是我们需要的ISO,后者是ISO的发布者使用私钥生成的签名文件,可以用来验证原文件是否完整且完全没有被修改。像这样进行验证:

# On Arch linux (-v = --verify)
pacman-key -v archlinux-version-x86_64.iso.sig
# On any system with GnuPG installed
gpg --keyserver-options auto-key-retrieve --verify archlinux-version-x86_64.iso.sig

看到"Good signature"之类的字眼,说明验证成功。

  1. 插入自己的闪存,然后以root身份使用 dd 命令写入ISO,注意of参数需要的是磁盘设备名而不是分区名 (甚至可以使用cat或cp)
dd bs=4M if=path/to/archlinux-version-x86_64.iso of=/dev/disk/by-id/usb-My_flash_drive conv=fsync oflag=direct status=progress

下面解释一下这行命令

  • bs=4M 这个参数指定了块大小(block size)为4MB, 使用较大的块理论上能提高复制的效率,但复制效率的上限被诸多因素定死,设置为4M即可。
  • conv=fsync 这个选项确保在复制过程中数据写入到磁盘时,所有的内存缓冲区都被刷新(fsync),以保证数据的完整性
  • oflag=direct 这个选项告诉 dd 直接写入到输出文件,绕过系统缓存。这样可以提高大文件写入时的性能,并减少系统缓存对其他进程的影响。
  • status=progress 这个选项告诉 dd 输出进度信息,使用户可以看到复制过程中的进度。

通过自己的闪存启动live系统

配置镜像源

把live系统上预装的这个没点用的工具停用了

systemctl stop reflector.service

打开/etc/pacman.d/mirrorlist, 把下面四行放在最前面:

Server = http://mirrors.ustc.edu.cn/archlinux/$repo/os/$arch
Server = https://mirrors.ustc.edu.cn/archlinux/$repo/os/$arch
Server = http://mirrors.tuna.tsinghua.edu.cn/archlinux/$repo/os/$arch
Server = https://mirrors.tuna.tsinghua.edu.cn/archlinux/$repo/os/$arch

连接网络,同步时间

  1. 连接网络。有线网络直接插上网线即可,无线网络可以用 iwctlwpa_suppliant + wpa_cli
  2. 更新时钟 在网络传输的各种较验中,时钟错误可能导致失败。使用以下命令更新live系统的时钟
timedatectl set-ntp true    #将系统时间与网络时间进行同步
timedatectl status          #检查服务状态

分区,文件系统和挂载; 生成fstab

  1. 分区方案:
  • Disk1
    • /boot (ESP): 1G
    • SWAP: 20G (slightly larger than physical memory)
    • /: remain space
  • Disk2
    • /home (不需要用fdisk进行分区)
  1. 创建文件系统
mkfs.fat -F 32 /dev/efi_system_partition # ESP
mkswap /dev/hdb1 # SWAP
mkfs.btrfs -L mylabel /dev/partition # /
mkfs.xfs device # /home
  1. 挂载和创建Btrfs子卷
mount /dev/partition_root /mnt
btrfs subvolume create /mnt/@current
mount -m /dev/partition_efi /mnt/@current/boot
mount -m /dev/partition_home /mnt/@current/home
  1. 生成fstab
  • 为了在启动我们安装的系统时能够自动挂载,需要生成fstab文件
mkdir /mnt/@current/etc
# 一定要用-U,即根据UUID挂载,否则默认根据盘名挂载,这在有多个硬盘时会因为顺序不定而出现问题。
genfstab -U /mnt/@current >> /mnt/@current/etc/fstab
  • 向fstab文件中追加一行swap分区 首先以root身份使用blkid命令查看UUID

(也可用lsblk)

lsblk -o NAME,FSTYPE,UUID

然后用cat等工具在fstab文件末尾追加以下文本, 其中discard选项是启用丢弃策略,即swapon -d

# <file system> <mount point>   <type>  <options>   <dump>  <pass>
UUID=your_swap_uuid	none    swap 	defaults,discard  	0 0
  • 检查没有错误
cat /mnt/@current/etc/fstab

向新系统安装基本包

  1. 最基本的包
pacstrap /mnt/@current base base-devel linux-zen linux-zen-headers linux-firmware
  • base: Arch Linux的基础包集合,包含了一个最小化的可用系统所需的核心软件包。它包括基本的文件系统工具、基本的 Shell(如bash)、核心库和其他基本工具。
  • base-devel: 包含了一组用于开发的软件包, 包括编译器、调试工具等。安装它是为了方便安装和编译其他软件包
  • linux-zen: zen内核, 相比默认内核桌面使用体验更佳。
  • linux-zen-headers: 与linux-zen包对应的内核头文件包, 它包含了用于编译内核模块和驱动程序的头文件。
  • linux-firmware: 各种硬件设备所需的固件文件
  1. 系统引导和正常运行必须的包
  • 安装微码
pacstrap /mnt/@current intel-ucode   #Intel
pacstrap /mnt/@current amd-ucode     #AMD
  • 安装引导程序
pacstrap /mnt/@current grub efibootmgr
  • 安装文件系统工具
pascstrap /mnt/@current btrfs-progs xfsprogs
  1. 安装联网工具, 备份恢复工具, 编辑器
pascstrap /mnt/@current wpa_suppliant dhcpcd rsync neovim

Chroot

arch-chroot /mnt/@current

新系统的引导

安装GRUB引导加载程序到/boot

grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB

确认/etc/default/grub中这一行为:

GRUB_CMDLINE_LINUX="rootflags=subvol=/@current"

最后生成GRUB所需的配置文件

grub-mkconfig -o /boot/grub/grub.cfg

设置主机名

nvim /etc/hostname

在这个文件中输入你想好的主机名即可,然后

nvim /etc/hosts

在这个文件下加入类似以下内容:

127.0.0.1   localhost
::1         localhost
127.0.1.1   my_hostname

root用户

给root用户添加密码

passwd root

添加一个wheel用户

# -m同时创建家目录,wheel是可sudo的用户组
useradd -m -G wheel -s /bin/bash user0
# 设置密码
passwd user0

sudo的配置

sudo在archlinux中属于基本组件,用来暂时暂时以其它用户的身份运行代码或执行文件。

sudo用户的配置文件是/etc/sudoers, 这个文件原则上只能通过visudo命令编辑

# 用环境变量指明编辑器是vim, 以后安装了neovim也可指定为nvim.
EDITOR=vim visudo

这个文件中我们只需要关注以下部分

##
## User privilege specification
##
root ALL=(ALL:ALL) ALL

## Uncomment to allow members of group wheel to execute any command
# %wheel ALL=(ALL:ALL) ALL

## Same thing without a password
%wheel ALL=(ALL:ALL) NOPASSWD: ALL

## Uncomment to allow members of group sudo to execute any command
# %sudo ALL=(ALL:ALL) ALL

## Uncomment to allow any user to run sudo if they know the password
## of the user they are running the command as (root by default).
# Defaults targetpw  # Ask for the password of the target user
# ALL ALL=(ALL:ALL) ALL  # WARNING: only use this together with 'Defaults targetpw'

下面以root ALL=(ALL:ALL) ALL为例讲解一下这几行命令

  • 开头的root表示要操作的用户,这里是root用户
  • 第1个ALL所在的字段表示主机名,这里用"ALL"说明指定的用户在任何主机上都能使用sudo命令
  • ()中的两个字段,第1个ALL表示可以以任何用户身份运行命令;第二个ALL表示可以以任何组身份运行命令。
  • 最后一个ALL表示允许执行所有命令

卸载和重启

exit                # 退回安装环境#
umount -R  /mnt     # 卸载新分区
reboot              # 重启

拔掉闪存,启动新系统

新系统上恢复到旧系统工作环境

恢复工作文件

挂载备份盘后,用po进行恢复

. /mnt/all/local/lib/bash/bash_tools/poshd/po.sh
po -r /mnt

恢复配置文件和软件包

使用前文的update脚本进行恢复

./update.sh recover