```bash #!/bin/sh set -eu SCRIPT_NAME=${0##*/} FSTAB=${ADD_SWAP_FSTAB:-/etc/fstab} PROC_SWAPS=${ADD_SWAP_PROC_SWAPS:-/proc/swaps} MEMINFO=${ADD_SWAP_MEMINFO:-/proc/meminfo} OS_RELEASE=${ADD_SWAP_OS_RELEASE:-/etc/os-release} SWAP_FILE= SIZE_MIB=0 CREATED=0 ACTIVE=0 FSTAB_MODIFIED=0 FSTAB_BACKUP= DONE=0 info() { printf '%s\n' "[信息] $*" } warn() { printf '%s\n' "[警告] $*" >&2 } die() { printf '%s\n' "[错误] $*" >&2 exit 1 } ask() { prompt=$1 default=$2 if [ -t 0 ] && [ -r /dev/tty ]; then printf '%s' "$prompt" >/dev/tty IFS= read -r answer /dev/null 2>&1 } is_active_swap() { awk -v path="$1" 'NR > 1 && $1 == path { found = 1 } END { exit !found }' "$PROC_SWAPS" } path_in_fstab() { [ -f "$FSTAB" ] || return 1 awk -v path="$1" '$1 == path && $3 == "swap" { found = 1 } END { exit !found }' "$FSTAB" } cleanup() { status=$? trap - 0 1 2 15 if [ "$DONE" -ne 1 ]; then if [ "$FSTAB_MODIFIED" -eq 1 ] && [ -n "$FSTAB_BACKUP" ] && [ -f "$FSTAB_BACKUP" ]; then cp -p "$FSTAB_BACKUP" "$FSTAB" || warn "无法恢复 $FSTAB,请手动检查。" fi if [ -n "$SWAP_FILE" ] && { [ "$ACTIVE" -eq 1 ] || is_active_swap "$SWAP_FILE"; }; then swapoff "$SWAP_FILE" >/dev/null 2>&1 || warn "无法停用 $SWAP_FILE,请手动处理。" fi if [ "$CREATED" -eq 1 ]; then rm -f "$SWAP_FILE" || warn "无法删除 $SWAP_FILE,请手动处理。" fi fi if [ -n "$FSTAB_BACKUP" ] && [ -f "$FSTAB_BACKUP" ]; then rm -f "$FSTAB_BACKUP" fi exit "$status" } trap cleanup 0 trap 'exit 130' 1 2 15 if [ "$(id -u)" -ne 0 ]; then if command_exists sudo && [ -f "$0" ]; then info "需要 root 权限,正在通过 sudo 重新运行。" trap - 0 1 2 15 exec sudo -- sh "$0" "$@" fi die "请以 root 身份运行:sudo sh $SCRIPT_NAME" fi OS_ID=unknown OS_NAME=Linux if [ -r "$OS_RELEASE" ]; then OS_ID=$(sed -n 's/^ID=//p' "$OS_RELEASE" | head -n 1 | tr -d '"') OS_NAME=$(sed -n 's/^PRETTY_NAME=//p' "$OS_RELEASE" | head -n 1 | sed 's/^"//; s/"$//') fi info "检测到系统:$OS_NAME" install_util_linux() { warn "系统缺少创建 swap 所需的 util-linux 工具。" confirm "是否现在安装?" y || die "缺少 mkswap、swapon 或 findmnt。" if command_exists apt-get; then apt-get update DEBIAN_FRONTEND=noninteractive apt-get install -y util-linux elif command_exists dnf; then dnf install -y util-linux elif command_exists yum; then yum install -y util-linux elif command_exists apk; then apk add --no-cache util-linux else die "不支持的包管理器。请手动安装 util-linux 后重试。" fi } if ! command_exists mkswap || ! command_exists swapon || ! command_exists findmnt; then install_util_linux fi command_exists mkswap || die "未找到 mkswap。" command_exists swapon || die "未找到 swapon。" command_exists swapoff || die "未找到 swapoff。" command_exists findmnt || die "未找到 findmnt。" RAM_MIB=$(awk '/^MemTotal:/ { print int($2 / 1024); exit }' "$MEMINFO") CURRENT_SWAP_MIB=$(awk 'NR > 1 { total += $3 } END { print int(total / 1024) }' "$PROC_SWAPS") printf '\n当前状态\n' printf ' 物理内存:%s MiB\n' "$RAM_MIB" printf ' 已启用 Swap:%s MiB\n' "$CURRENT_SWAP_MIB" if [ "$CURRENT_SWAP_MIB" -gt 0 ]; then awk 'NR == 1 { next } { printf " %s (%d MiB)\n", $1, int($3 / 1024) }' "$PROC_SWAPS" fi DEFAULT_FILE=/swapfile index=2 while [ -e "$DEFAULT_FILE" ] || [ -L "$DEFAULT_FILE" ] || path_in_fstab "$DEFAULT_FILE"; do DEFAULT_FILE=/swapfile$index index=$((index + 1)) done while :; do printf '\n' ask "新 Swap 文件路径 [$DEFAULT_FILE]:" "$DEFAULT_FILE" SWAP_FILE=$REPLY case "$SWAP_FILE" in /*) ;; *) warn "必须使用绝对路径。"; continue ;; esac case "$SWAP_FILE" in *[[:space:]]*) warn "路径不能包含空白字符。"; continue ;; esac [ "$SWAP_FILE" != / ] || { warn "不能使用根目录。"; continue; } [ ! -e "$SWAP_FILE" ] && [ ! -L "$SWAP_FILE" ] || { warn "$SWAP_FILE 已存在,请换一个路径。"; continue; } path_in_fstab "$SWAP_FILE" && { warn "$FSTAB 中已有该路径,请换一个路径。"; continue; } SWAP_DIR=${SWAP_FILE%/*} [ -n "$SWAP_DIR" ] || SWAP_DIR=/ [ -d "$SWAP_DIR" ] || { warn "目录 $SWAP_DIR 不存在。"; continue; } break done if [ "$RAM_MIB" -le 2048 ]; then DEFAULT_CHOICE=2 else DEFAULT_CHOICE=3 fi while :; do printf '\n请选择新增容量:\n' printf ' 1) 1 GiB\n' printf ' 2) 2 GiB\n' printf ' 3) 4 GiB\n' printf ' 4) 8 GiB\n' printf ' 5) 自定义(如 1536M、6G)\n' ask "选择 [$DEFAULT_CHOICE]:" "$DEFAULT_CHOICE" case "$REPLY" in 1) SIZE_MIB=1024 ;; 2) SIZE_MIB=2048 ;; 3) SIZE_MIB=4096 ;; 4) SIZE_MIB=8192 ;; 5) ask "请输入容量:" "" custom_size=$(printf '%s' "$REPLY" | tr 'gGmM' 'GGMM') case "$custom_size" in *G) number=${custom_size%G}; multiplier=1024 ;; *M) number=${custom_size%M}; multiplier=1 ;; *) number=; multiplier=1 ;; esac case "$number" in ''|*[!0-9]*) warn "格式无效,请输入整数加 M 或 G。"; continue ;; esac SIZE_MIB=$((number * multiplier)) ;; *) warn "无效选项。"; continue ;; esac [ "$SIZE_MIB" -ge 64 ] || { warn "容量不能小于 64 MiB。"; continue; } break done FS_TYPE=$(findmnt -n -o FSTYPE -T "$SWAP_DIR" 2>/dev/null || true) [ -n "$FS_TYPE" ] || die "无法识别 $SWAP_DIR 所在文件系统。" case "$FS_TYPE" in nfs*|cifs|smb*|overlay|tmpfs|fuse*) die "$FS_TYPE 不支持安全创建 swap 文件。" ;; esac AVAILABLE_KIB=$(df -Pk "$SWAP_DIR" | awk 'END { print $4 }') case "$AVAILABLE_KIB" in ''|*[!0-9]*) die "无法读取可用磁盘空间。" ;; esac REQUIRED_KIB=$((SIZE_MIB * 1024 + 131072)) if [ "$AVAILABLE_KIB" -lt "$REQUIRED_KIB" ]; then die "磁盘空间不足:至少需要 $((REQUIRED_KIB / 1024)) MiB,当前可用 $((AVAILABLE_KIB / 1024)) MiB。" fi printf '\n即将执行\n' printf ' 系统:%s (%s)\n' "$OS_NAME" "$OS_ID" printf ' 文件:%s\n' "$SWAP_FILE" printf ' 容量:%s MiB\n' "$SIZE_MIB" printf ' 文件系统:%s\n' "$FS_TYPE" printf ' 持久化:写入 %s\n' "$FSTAB" [ "$FS_TYPE" = btrfs ] && warn "Btrfs 上的 swap 文件不能被快照包含。" confirm "确认创建?" n || die "已取消,系统未做更改。" umask 077 if [ "$FS_TYPE" = btrfs ]; then if ! command_exists chattr; then warn "Btrfs 需要 chattr 来关闭写时复制。" confirm "是否现在安装?" y || die "缺少 chattr。" if command_exists apt-get; then DEBIAN_FRONTEND=noninteractive apt-get install -y e2fsprogs elif command_exists dnf; then dnf install -y e2fsprogs elif command_exists yum; then yum install -y e2fsprogs elif command_exists apk; then apk add --no-cache e2fsprogs-extra else die "请手动安装 chattr 后重试。" fi fi : >"$SWAP_FILE" CREATED=1 chattr +C "$SWAP_FILE" || die "无法为 Btrfs swap 文件设置 No_COW。" fi info "正在分配 $SIZE_MIB MiB 空间..." if command_exists fallocate && fallocate -l "${SIZE_MIB}M" "$SWAP_FILE"; then CREATED=1 else warn "fallocate 不可用,改用 dd 分配空间,这可能需要一些时间。" rm -f "$SWAP_FILE" CREATED=0 if [ "$FS_TYPE" = btrfs ]; then : >"$SWAP_FILE" CREATED=1 chattr +C "$SWAP_FILE" || die "无法为 Btrfs swap 文件设置 No_COW。" fi dd if=/dev/zero of="$SWAP_FILE" bs=1M count="$SIZE_MIB" CREATED=1 fi chmod 600 "$SWAP_FILE" command_exists restorecon && restorecon "$SWAP_FILE" >/dev/null 2>&1 || true mkswap "$SWAP_FILE" swapon "$SWAP_FILE" ACTIVE=1 is_active_swap "$SWAP_FILE" || die "swap 启用后未出现在 $PROC_SWAPS 中。" FSTAB_BACKUP=$(mktemp "${TMPDIR:-/tmp}/add-swap-fstab.XXXXXX") cp -p "$FSTAB" "$FSTAB_BACKUP" FSTAB_MODIFIED=1 printf '\n%s none swap sw 0 0\n' "$SWAP_FILE" >>"$FSTAB" if command_exists systemctl && systemctl is-system-running >/dev/null 2>&1; then systemctl daemon-reload || warn "systemd 配置重载失败;重启时仍会读取 $FSTAB。" fi DONE=1 rm -f "$FSTAB_BACKUP" FSTAB_BACKUP= NEW_TOTAL_MIB=$(awk 'NR > 1 { total += $3 } END { print int(total / 1024) }' "$PROC_SWAPS") printf '\n完成:%s 已启用并写入 %s。\n' "$SWAP_FILE" "$FSTAB" printf '当前 Swap 总量:%s MiB\n' "$NEW_TOTAL_MIB" printf '可用以下命令确认:swapon --show\n' ``` Loading... ```bash #!/bin/sh set -eu SCRIPT_NAME=${0##*/} FSTAB=${ADD_SWAP_FSTAB:-/etc/fstab} PROC_SWAPS=${ADD_SWAP_PROC_SWAPS:-/proc/swaps} MEMINFO=${ADD_SWAP_MEMINFO:-/proc/meminfo} OS_RELEASE=${ADD_SWAP_OS_RELEASE:-/etc/os-release} SWAP_FILE= SIZE_MIB=0 CREATED=0 ACTIVE=0 FSTAB_MODIFIED=0 FSTAB_BACKUP= DONE=0 info() { printf '%s\n' "[信息] $*" } warn() { printf '%s\n' "[警告] $*" >&2 } die() { printf '%s\n' "[错误] $*" >&2 exit 1 } ask() { prompt=$1 default=$2 if [ -t 0 ] && [ -r /dev/tty ]; then printf '%s' "$prompt" >/dev/tty IFS= read -r answer </dev/tty || answer= else printf '%s' "$prompt" IFS= read -r answer || answer= fi if [ -z "$answer" ]; then answer=$default fi REPLY=$answer } confirm() { prompt=$1 default=${2:-n} if [ "$default" = y ]; then suffix='[Y/n] ' else suffix='[y/N] ' fi ask "$prompt $suffix" "$default" case "$REPLY" in y|Y|yes|YES|Yes) return 0 ;; *) return 1 ;; esac } command_exists() { command -v "$1" >/dev/null 2>&1 } is_active_swap() { awk -v path="$1" 'NR > 1 && $1 == path { found = 1 } END { exit !found }' "$PROC_SWAPS" } path_in_fstab() { [ -f "$FSTAB" ] || return 1 awk -v path="$1" '$1 == path && $3 == "swap" { found = 1 } END { exit !found }' "$FSTAB" } cleanup() { status=$? trap - 0 1 2 15 if [ "$DONE" -ne 1 ]; then if [ "$FSTAB_MODIFIED" -eq 1 ] && [ -n "$FSTAB_BACKUP" ] && [ -f "$FSTAB_BACKUP" ]; then cp -p "$FSTAB_BACKUP" "$FSTAB" || warn "无法恢复 $FSTAB,请手动检查。" fi if [ -n "$SWAP_FILE" ] && { [ "$ACTIVE" -eq 1 ] || is_active_swap "$SWAP_FILE"; }; then swapoff "$SWAP_FILE" >/dev/null 2>&1 || warn "无法停用 $SWAP_FILE,请手动处理。" fi if [ "$CREATED" -eq 1 ]; then rm -f "$SWAP_FILE" || warn "无法删除 $SWAP_FILE,请手动处理。" fi fi if [ -n "$FSTAB_BACKUP" ] && [ -f "$FSTAB_BACKUP" ]; then rm -f "$FSTAB_BACKUP" fi exit "$status" } trap cleanup 0 trap 'exit 130' 1 2 15 if [ "$(id -u)" -ne 0 ]; then if command_exists sudo && [ -f "$0" ]; then info "需要 root 权限,正在通过 sudo 重新运行。" trap - 0 1 2 15 exec sudo -- sh "$0" "$@" fi die "请以 root 身份运行:sudo sh $SCRIPT_NAME" fi OS_ID=unknown OS_NAME=Linux if [ -r "$OS_RELEASE" ]; then OS_ID=$(sed -n 's/^ID=//p' "$OS_RELEASE" | head -n 1 | tr -d '"') OS_NAME=$(sed -n 's/^PRETTY_NAME=//p' "$OS_RELEASE" | head -n 1 | sed 's/^"//; s/"$//') fi info "检测到系统:$OS_NAME" install_util_linux() { warn "系统缺少创建 swap 所需的 util-linux 工具。" confirm "是否现在安装?" y || die "缺少 mkswap、swapon 或 findmnt。" if command_exists apt-get; then apt-get update DEBIAN_FRONTEND=noninteractive apt-get install -y util-linux elif command_exists dnf; then dnf install -y util-linux elif command_exists yum; then yum install -y util-linux elif command_exists apk; then apk add --no-cache util-linux else die "不支持的包管理器。请手动安装 util-linux 后重试。" fi } if ! command_exists mkswap || ! command_exists swapon || ! command_exists findmnt; then install_util_linux fi command_exists mkswap || die "未找到 mkswap。" command_exists swapon || die "未找到 swapon。" command_exists swapoff || die "未找到 swapoff。" command_exists findmnt || die "未找到 findmnt。" RAM_MIB=$(awk '/^MemTotal:/ { print int($2 / 1024); exit }' "$MEMINFO") CURRENT_SWAP_MIB=$(awk 'NR > 1 { total += $3 } END { print int(total / 1024) }' "$PROC_SWAPS") printf '\n当前状态\n' printf ' 物理内存:%s MiB\n' "$RAM_MIB" printf ' 已启用 Swap:%s MiB\n' "$CURRENT_SWAP_MIB" if [ "$CURRENT_SWAP_MIB" -gt 0 ]; then awk 'NR == 1 { next } { printf " %s (%d MiB)\n", $1, int($3 / 1024) }' "$PROC_SWAPS" fi DEFAULT_FILE=/swapfile index=2 while [ -e "$DEFAULT_FILE" ] || [ -L "$DEFAULT_FILE" ] || path_in_fstab "$DEFAULT_FILE"; do DEFAULT_FILE=/swapfile$index index=$((index + 1)) done while :; do printf '\n' ask "新 Swap 文件路径 [$DEFAULT_FILE]:" "$DEFAULT_FILE" SWAP_FILE=$REPLY case "$SWAP_FILE" in /*) ;; *) warn "必须使用绝对路径。"; continue ;; esac case "$SWAP_FILE" in *[[:space:]]*) warn "路径不能包含空白字符。"; continue ;; esac [ "$SWAP_FILE" != / ] || { warn "不能使用根目录。"; continue; } [ ! -e "$SWAP_FILE" ] && [ ! -L "$SWAP_FILE" ] || { warn "$SWAP_FILE 已存在,请换一个路径。"; continue; } path_in_fstab "$SWAP_FILE" && { warn "$FSTAB 中已有该路径,请换一个路径。"; continue; } SWAP_DIR=${SWAP_FILE%/*} [ -n "$SWAP_DIR" ] || SWAP_DIR=/ [ -d "$SWAP_DIR" ] || { warn "目录 $SWAP_DIR 不存在。"; continue; } break done if [ "$RAM_MIB" -le 2048 ]; then DEFAULT_CHOICE=2 else DEFAULT_CHOICE=3 fi while :; do printf '\n请选择新增容量:\n' printf ' 1) 1 GiB\n' printf ' 2) 2 GiB\n' printf ' 3) 4 GiB\n' printf ' 4) 8 GiB\n' printf ' 5) 自定义(如 1536M、6G)\n' ask "选择 [$DEFAULT_CHOICE]:" "$DEFAULT_CHOICE" case "$REPLY" in 1) SIZE_MIB=1024 ;; 2) SIZE_MIB=2048 ;; 3) SIZE_MIB=4096 ;; 4) SIZE_MIB=8192 ;; 5) ask "请输入容量:" "" custom_size=$(printf '%s' "$REPLY" | tr 'gGmM' 'GGMM') case "$custom_size" in *G) number=${custom_size%G}; multiplier=1024 ;; *M) number=${custom_size%M}; multiplier=1 ;; *) number=; multiplier=1 ;; esac case "$number" in ''|*[!0-9]*) warn "格式无效,请输入整数加 M 或 G。"; continue ;; esac SIZE_MIB=$((number * multiplier)) ;; *) warn "无效选项。"; continue ;; esac [ "$SIZE_MIB" -ge 64 ] || { warn "容量不能小于 64 MiB。"; continue; } break done FS_TYPE=$(findmnt -n -o FSTYPE -T "$SWAP_DIR" 2>/dev/null || true) [ -n "$FS_TYPE" ] || die "无法识别 $SWAP_DIR 所在文件系统。" case "$FS_TYPE" in nfs*|cifs|smb*|overlay|tmpfs|fuse*) die "$FS_TYPE 不支持安全创建 swap 文件。" ;; esac AVAILABLE_KIB=$(df -Pk "$SWAP_DIR" | awk 'END { print $4 }') case "$AVAILABLE_KIB" in ''|*[!0-9]*) die "无法读取可用磁盘空间。" ;; esac REQUIRED_KIB=$((SIZE_MIB * 1024 + 131072)) if [ "$AVAILABLE_KIB" -lt "$REQUIRED_KIB" ]; then die "磁盘空间不足:至少需要 $((REQUIRED_KIB / 1024)) MiB,当前可用 $((AVAILABLE_KIB / 1024)) MiB。" fi printf '\n即将执行\n' printf ' 系统:%s (%s)\n' "$OS_NAME" "$OS_ID" printf ' 文件:%s\n' "$SWAP_FILE" printf ' 容量:%s MiB\n' "$SIZE_MIB" printf ' 文件系统:%s\n' "$FS_TYPE" printf ' 持久化:写入 %s\n' "$FSTAB" [ "$FS_TYPE" = btrfs ] && warn "Btrfs 上的 swap 文件不能被快照包含。" confirm "确认创建?" n || die "已取消,系统未做更改。" umask 077 if [ "$FS_TYPE" = btrfs ]; then if ! command_exists chattr; then warn "Btrfs 需要 chattr 来关闭写时复制。" confirm "是否现在安装?" y || die "缺少 chattr。" if command_exists apt-get; then DEBIAN_FRONTEND=noninteractive apt-get install -y e2fsprogs elif command_exists dnf; then dnf install -y e2fsprogs elif command_exists yum; then yum install -y e2fsprogs elif command_exists apk; then apk add --no-cache e2fsprogs-extra else die "请手动安装 chattr 后重试。" fi fi : >"$SWAP_FILE" CREATED=1 chattr +C "$SWAP_FILE" || die "无法为 Btrfs swap 文件设置 No_COW。" fi info "正在分配 $SIZE_MIB MiB 空间..." if command_exists fallocate && fallocate -l "${SIZE_MIB}M" "$SWAP_FILE"; then CREATED=1 else warn "fallocate 不可用,改用 dd 分配空间,这可能需要一些时间。" rm -f "$SWAP_FILE" CREATED=0 if [ "$FS_TYPE" = btrfs ]; then : >"$SWAP_FILE" CREATED=1 chattr +C "$SWAP_FILE" || die "无法为 Btrfs swap 文件设置 No_COW。" fi dd if=/dev/zero of="$SWAP_FILE" bs=1M count="$SIZE_MIB" CREATED=1 fi chmod 600 "$SWAP_FILE" command_exists restorecon && restorecon "$SWAP_FILE" >/dev/null 2>&1 || true mkswap "$SWAP_FILE" swapon "$SWAP_FILE" ACTIVE=1 is_active_swap "$SWAP_FILE" || die "swap 启用后未出现在 $PROC_SWAPS 中。" FSTAB_BACKUP=$(mktemp "${TMPDIR:-/tmp}/add-swap-fstab.XXXXXX") cp -p "$FSTAB" "$FSTAB_BACKUP" FSTAB_MODIFIED=1 printf '\n%s none swap sw 0 0\n' "$SWAP_FILE" >>"$FSTAB" if command_exists systemctl && systemctl is-system-running >/dev/null 2>&1; then systemctl daemon-reload || warn "systemd 配置重载失败;重启时仍会读取 $FSTAB。" fi DONE=1 rm -f "$FSTAB_BACKUP" FSTAB_BACKUP= NEW_TOTAL_MIB=$(awk 'NR > 1 { total += $3 } END { print int(total / 1024) }' "$PROC_SWAPS") printf '\n完成:%s 已启用并写入 %s。\n' "$SWAP_FILE" "$FSTAB" printf '当前 Swap 总量:%s MiB\n' "$NEW_TOTAL_MIB" printf '可用以下命令确认:swapon --show\n' ``` [AD]【腾讯云】年度爆款2核2G4M云服务器118元/年,新老用户同享 最后修改:2026 年 09 月 27 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 1 如果觉得我的文章对你有用,请随意赞赏