diff --git a/.gitignore b/.gitignore index a547bf3..a31ad8f 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ dist-ssr *.njsproj *.sln *.sw? +/debug/ diff --git a/README.md b/README.md index 50ca099..b171080 100644 --- a/README.md +++ b/README.md @@ -203,8 +203,13 @@ LVM 场景下,VG 的可用空间不足。脚本会先尝试扩展 PV 占满磁 ``` LinuxDiskAutoExpansion/ ├── src/ -│ └── main.sh # 主脚本 -├── README.md # 项目说明文档 +│ └── main.sh # 主脚本 +├── debug/ +│ └── logs/ # 调试日志 +│ ├── ubuntu.log +│ └── centos.log +├── README.md # 项目说明文档 +├── USAGE.md # 详细使用文档 └── LinuxDiskAutoExpansion.iml ``` diff --git a/USAGE.md b/USAGE.md index ccb7fe3..cbedf81 100644 --- a/USAGE.md +++ b/USAGE.md @@ -88,7 +88,7 @@ sda 8:0 0 50G 0 disk **方式一:Git 克隆(推荐)** ```bash -git clone https://github.com/your-repo/LinuxDiskAutoExpansion.git +git clone https://git.opensource-studio.info/database-mysql/LinuxDiskAutoExpansion.git cd LinuxDiskAutoExpansion ``` diff --git a/src/main.sh b/src/main.sh index d3ea67c..b841a14 100644 --- a/src/main.sh +++ b/src/main.sh @@ -131,6 +131,12 @@ ensure_dependencies() { fi fi + # 检查 parted 或 sfdisk(用于获取分区信息) + if ! command -v parted &>/dev/null && ! command -v sfdisk &>/dev/null; then + info "parted/sfdisk 未安装,正在安装 parted..." + install_package "parted" + fi + # 检查 lsblk if ! command -v lsblk &>/dev/null; then install_package "util-linux" @@ -253,6 +259,20 @@ check_expandable_space() { fi } +#--------------------------------------- +# 安全提取数值(去除小数部分,兼容 set -e) +#--------------------------------------- +parse_int() { + # 从字符串中提取整数部分,忽略小数和后缀 + # 输入: "8217.60" / "8.22g" / " 100 " 等 + # 输出: 纯整数字符串,如 "8217" / "8" / "100" + local val="$1" + # 去除首尾空格 + val=$(echo "$val" | xargs) + # 提取整数部分(兼容小数点) + echo "$val" | awk '{printf "%d", $1}' +} + #--------------------------------------- # LVM 模式:检查 VG 剩余空间 #--------------------------------------- @@ -264,21 +284,24 @@ check_lvm_space() { fi info "根目录所在卷组 (VG): $ROOT_VG" - # 获取 VG 的可用空间(单位:字节) - VG_FREE_BYTES=$(vgs -no vg_free "$ROOT_VG" --units b 2>/dev/null | xargs) - # 转换为人类可读格式 - VG_FREE_HUMAN=$(vgs -no vg_free "$ROOT_VG" --units g 2>/dev/null | xargs) + # 获取 VG 的可用空间(人类可读格式,用于显示) + VG_FREE_HUMAN=$(vgs -no vg_free "$ROOT_VG" --units g --nosuffix 2>/dev/null | xargs) info "卷组 $ROOT_VG 可用空间: ${VG_FREE_HUMAN}G" - # 检查 VG 是否有剩余空间 - VG_FREE_NUM=$(vgs -no vg_free "$ROOT_VG" --units m 2>/dev/null | xargs | grep -oP '^\d+') + # 检查 VG 是否有剩余空间(以 MB 为单位判断) + local vg_free_raw + vg_free_raw=$(vgs -no vg_free "$ROOT_VG" --units m --nosuffix 2>/dev/null) + VG_FREE_NUM=$(parse_int "$vg_free_raw") + info "卷组可用空间: ${VG_FREE_NUM}MB" + if [[ -z "$VG_FREE_NUM" ]] || [[ "$VG_FREE_NUM" -lt 100 ]]; then # VG 没有空间,检查是否是因为 PV 没有占满磁盘 info "卷组可用空间不足,将检查是否需要扩展物理卷 (PV)..." expand_pv_if_needed # 重新获取可用空间 - VG_FREE_NUM=$(vgs -no vg_free "$ROOT_VG" --units m 2>/dev/null | xargs | grep -oP '^\d+') - VG_FREE_HUMAN=$(vgs -no vg_free "$ROOT_VG" --units g 2>/dev/null | xargs) + vg_free_raw=$(vgs -no vg_free "$ROOT_VG" --units m --nosuffix 2>/dev/null) + VG_FREE_NUM=$(parse_int "$vg_free_raw") + VG_FREE_HUMAN=$(vgs -no vg_free "$ROOT_VG" --units g --nosuffix 2>/dev/null | xargs) if [[ -z "$VG_FREE_NUM" ]] || [[ "$VG_FREE_NUM" -lt 100 ]]; then die "卷组 $ROOT_VG 没有足够的可用空间进行扩容(需要至少 100MB)" fi @@ -314,17 +337,22 @@ expand_pv_if_needed() { # 检查磁盘是否有未分配空间 local disk_size sector_size total_sectors part_end_sector - disk_size=$(blockdev --getsize64 "$parent_disk" 2>/dev/null) - sector_size=$(blockdev --getss "$parent_disk" 2>/dev/null) + disk_size=$(blockdev --getsize64 "$parent_disk" 2>/dev/null) || disk_size=0 + sector_size=$(blockdev --getss "$parent_disk" 2>/dev/null) || sector_size=512 total_sectors=$((disk_size / sector_size)) if [[ "$pv_is_partition" == true ]]; then local part_num part_num=$(get_partition_number "$pv_dev") - # 获取分区结束扇区 - part_end_sector=$(parted -s "$parent_disk" unit s print 2>/dev/null | \ - awk -v num="$part_num" '$1 == num { gsub(/s/,"",$3); print $3 }') + # 获取分区结束扇区(优先使用 parted,备选 sfdisk) + if command -v parted &>/dev/null; then + part_end_sector=$(parted -s "$parent_disk" unit s print 2>/dev/null | \ + awk -v num="$part_num" '$1 == num { gsub(/s/,"",$3); print $3 }') + elif command -v sfdisk &>/dev/null; then + part_end_sector=$(sfdisk -d "$parent_disk" 2>/dev/null | \ + awk -F'[=,]' -v num="$part_num" '{for(i=1;i<=NF;i++) if($i ~ /start=/) {split($i,a,"="); if(a[2]+0 > 0) start=a[2]} if($i ~ /size=/) {split($i,b,"="); size=b[2]}} NR==num {print start+size-1}') + fi if [[ -z "$part_end_sector" ]]; then warn "无法获取分区结束扇区,尝试直接使用 pvresize..." @@ -376,14 +404,19 @@ check_partition_space() { # 获取磁盘和分区的扇区信息 local disk_size sector_size total_sectors - disk_size=$(blockdev --getsize64 "$ROOT_DISK" 2>/dev/null) - sector_size=$(blockdev --getss "$ROOT_DISK" 2>/dev/null) + disk_size=$(blockdev --getsize64 "$ROOT_DISK" 2>/dev/null) || disk_size=0 + sector_size=$(blockdev --getss "$ROOT_DISK" 2>/dev/null) || sector_size=512 total_sectors=$((disk_size / sector_size)) - # 获取分区结束扇区 + # 获取分区结束扇区(优先使用 parted,备选 sfdisk) local part_end_sector - part_end_sector=$(parted -s "$ROOT_DISK" unit s print 2>/dev/null | \ - awk -v num="$part_num" '$1 == num { gsub(/s/,"",$3); print $3 }') + if command -v parted &>/dev/null; then + part_end_sector=$(parted -s "$ROOT_DISK" unit s print 2>/dev/null | \ + awk -v num="$part_num" '$1 == num { gsub(/s/,"",$3); print $3 }') + elif command -v sfdisk &>/dev/null; then + part_end_sector=$(sfdisk -d "$ROOT_DISK" 2>/dev/null | \ + awk -F'[=,]' -v num="$part_num" '{for(i=1;i<=NF;i++) if($i ~ /start=/) {split($i,a,"="); if(a[2]+0 > 0) start=a[2]} if($i ~ /size=/) {split($i,b,"="); size=b[2]}} NR==num {print start+size-1}') + fi if [[ -z "$part_end_sector" ]]; then die "无法获取分区 $ROOT_DEVICE 的结束扇区"