ubuntu多块硬盘挂载到同一目录LVM方式

挂载脚本 lvm_mount.sh

根据用户选择挂载目录以及挂载硬盘

复制代码
#!/bin/bash

# LVM 自动挂载脚本(包含完整 LVM 安装)
set -e

echo "=== LVM 多磁盘挂载脚本 ==="
echo ""

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# 函数:检查并安装 LVM
install_lvm() {
    echo -e "${YELLOW}检查 LVM 安装...${NC}"
    
    # 检查必要的 LVM 命令
    local lvm_commands=("pvcreate" "vgcreate" "lvcreate" "lvs" "vgs" "pvs")
    local missing_commands=()
    
    for cmd in "${lvm_commands[@]}"; do
        if ! command -v "$cmd" &> /dev/null; then
            missing_commands+=("$cmd")
        fi
    done
    
    if [ ${#missing_commands[@]} -eq 0 ]; then
        echo -e "${GREEN}✓ LVM 已安装${NC}"
        return 0
    fi
    
    echo -e "${YELLOW}安装 LVM2 包...${NC}"
    
    # 更新包列表
    if ! apt update; then
        echo -e "${RED}错误: 无法更新包列表${NC}"
        return 1
    fi
    
    # 安装 LVM2
    if apt install -y lvm2; then
        echo -e "${GREEN}✓ LVM2 安装成功${NC}"
        
        # 检查并加载内核模块
        echo -e "${YELLOW}检查内核模块...${NC}"
        local modules=("dm_mod" "dm_thin_pool")
        for module in "${modules[@]}"; do
            if ! lsmod | grep -q "$module"; then
                if modprobe "$module"; then
                    echo -e "${GREEN}✓ 加载内核模块: $module${NC}"
                else
                    echo -e "${YELLOW}⚠ 无法加载模块: $module${NC}"
                fi
            fi
        done
        
        # 启动 LVM 服务
        systemctl enable --now lvm2-lvmetad.service &>/dev/null
        systemctl enable --now lvm2-lvmpolld.service &>/dev/null
        
        return 0
    else
        echo -e "${RED}错误: LVM2 安装失败${NC}"
        return 1
    fi
}

# 函数:检查 root 权限
check_root() {
    if [ "$EUID" -ne 0 ]; then
        echo -e "${RED}错误: 请使用 sudo 运行此脚本${NC}"
        echo "使用方法: sudo $0"
        exit 1
    fi
}

# 函数:显示磁盘信息
show_disk_info() {
    echo -e "${GREEN}=== 当前系统磁盘信息 ===${NC}"
    echo "设备名称    大小      类型  挂载点       文件系统   型号"
    echo "------------------------------------------------------------"
    lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE,MODEL | grep -v loop
    echo ""
}

# 函数:获取可用磁盘
get_available_disks() {
    echo -e "${YELLOW}扫描可用磁盘...${NC}"
    available_disks=()
    
    # 使用 lsblk 获取磁盘信息
    while IFS= read -r line; do
        if [ -z "$line" ]; then
            continue
        fi
        
        disk_name=$(echo "$line" | awk '{print $1}')
        disk_type=$(echo "$line" | awk '{print $3}')
        mountpoint=$(echo "$line" | awk '{print $4}')
        fstype=$(echo "$line" | awk '{print $5}')
        
        # 检查是否是磁盘类型且未挂载
        if [ "$disk_type" == "disk" ] && [ -z "$mountpoint" ] && [ -z "$fstype" ]; then
            # 进一步检查是否是系统盘(通过检查是否有子分区被挂载为 / )
            if ! lsblk "/dev/$disk_name" | grep -q "/$"; then
                # 检查是否已经有文件系统
                if ! blkid "/dev/$disk_name" &> /dev/null; then
                    available_disks+=("$disk_name")
                    echo -e "${GREEN}  ✓ 找到可用磁盘: /dev/$disk_name${NC}"
                else
                    echo -e "${YELLOW}  ⓘ 跳过 /dev/$disk_name (已有文件系统)${NC}"
                fi
            else
                echo -e "${YELLOW}  ⓘ 跳过 /dev/$disk_name (系统盘)${NC}"
            fi
        fi
    done < <(lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE | tail -n +2)
    
    if [ ${#available_disks[@]} -eq 0 ]; then
        echo -e "${RED}未找到可用的磁盘!${NC}"
        echo "请确认:"
        echo "1. 磁盘已正确插入"
        echo "2. 磁盘未被挂载"
        echo "3. 磁盘没有文件系统"
        return 1
    fi
    
    return 0
}

# 函数:选择磁盘
select_disks() {
    local available_disks=("$@")
    selected_disks=()
    
    echo -e "${GREEN}可用的磁盘:${NC}"
    for i in "${!available_disks[@]}"; do
        disk_info=$(lsblk -b -d -o NAME,SIZE,MODEL "/dev/${available_disks[$i]}" | tail -1)
        disk_size=$(echo "$disk_info" | awk '{print $2}')
        disk_model=$(echo "$disk_info" | awk '{for(i=3;i<=NF;i++) printf $i" "}')
        size_gb=$(echo "scale=2; $disk_size/1024/1024/1024" | bc)
        echo "  $((i+1)). /dev/${available_disks[$i]} (${size_gb} GB) - $disk_model"
    done
    
    echo ""
    echo -e "${YELLOW}请选择要用于 LVM 的磁盘:${NC}"
    echo "输入数字,多个磁盘用空格分隔(例如: 1 2 3)"
    read -r -p "选择: " choices
    
    if [ -z "$choices" ]; then
        echo -e "${RED}错误: 未选择任何磁盘${NC}"
        return 1
    fi
    
    for choice in $choices; do
        index=$((choice-1))
        if [ $index -ge 0 ] && [ $index -lt ${#available_disks[@]} ]; then
            selected_disks+=("/dev/${available_disks[$index]}")
            echo -e "${GREEN}  ✓ 选择: /dev/${available_disks[$index]}${NC}"
        else
            echo -e "${RED}错误: 无效选择 '$choice'${NC}"
            return 1
        fi
    done
    
    if [ ${#selected_disks[@]} -eq 0 ]; then
        echo -e "${RED}错误: 未选择任何磁盘${NC}"
        return 1
    fi
    
    return 0
}

# 函数:输入挂载点
get_mount_point() {
    echo ""
    echo -e "${YELLOW}请输入挂载点目录:${NC}"
    echo "示例:"
    echo "  /home/mnt       # 家目录下的 mnt 文件夹"
    echo "  /data          # 根目录下的 data 文件夹" 
    echo "  /mnt/storage   # 默认的 mnt 目录"
    echo "  /app/data      # 应用数据目录"
    read -r -p "挂载点路径: " mount_point
    
    # 检查输入是否为空
    if [ -z "$mount_point" ]; then
        echo -e "${YELLOW}使用默认挂载点: /mnt/storage${NC}"
        mount_point="/mnt/storage"
    fi
    
    # 移除末尾的斜杠
    mount_point="${mount_point%/}"
    
    # 检查路径是否以斜杠开头
    if [[ ! "$mount_point" =~ ^/ ]]; then
        echo -e "${RED}错误: 挂载点必须是绝对路径(以 / 开头)${NC}"
        return 1
    fi
    
    # 检查目录是否已存在且被挂载
    if mountpoint -q "$mount_point" 2>/dev/null; then
        echo -e "${RED}错误: 目录 $mount_point 已被挂载${NC}"
        return 1
    fi
    
    echo -e "${GREEN}挂载点设置为: $mount_point${NC}"
    return 0
}

# 函数:确认操作
confirm_operation() {
    echo ""
    echo -e "${YELLOW}=== 操作摘要 ===${NC}"
    echo "选择的磁盘: ${selected_disks[*]}"
    echo "卷组名称: $vg_name"
    echo "逻辑卷名称: $lv_name"
    echo "挂载点: $mount_point"
    echo "文件系统: $filesystem"
    echo ""
    echo -e "${RED}警告: 这将格式化所选磁盘,所有数据将被删除!${NC}"
    echo ""
    
    read -r -p "确认执行以上操作?(输入 'YES' 继续): " confirm
    if [ "$confirm" != "YES" ]; then
        echo -e "${YELLOW}操作已取消${NC}"
        exit 0
    fi
}

# 函数:执行 LVM 配置
setup_lvm() {
    local disks=("$@")
    
    echo -e "${GREEN}=== 开始 LVM 配置 ===${NC}"
    
    # 1. 创建物理卷
    echo "步骤 1: 创建物理卷..."
    for disk in "${disks[@]}"; do
        echo -e "  - 处理 $disk"
        if pvcreate "$disk"; then
            echo -e "    ${GREEN}✓ 物理卷创建成功${NC}"
        else
            echo -e "    ${RED}✗ 物理卷创建失败${NC}"
            return 1
        fi
    done
    
    # 2. 创建卷组
    echo "步骤 2: 创建卷组 '$vg_name'..."
    if vgcreate "$vg_name" "${disks[@]}"; then
        echo -e "  ${GREEN}✓ 卷组创建成功${NC}"
    else
        echo -e "  ${RED}✗ 卷组创建失败${NC}"
        return 1
    fi
    
    # 3. 创建逻辑卷
    echo "步骤 3: 创建逻辑卷 '$lv_name'..."
    if lvcreate -n "$lv_name" -l 100%FREE "$vg_name"; then
        echo -e "  ${GREEN}✓ 逻辑卷创建成功${NC}"
    else
        echo -e "  ${RED}✗ 逻辑卷创建失败${NC}"
        return 1
    fi
    
    # 4. 格式化
    echo "步骤 4: 格式化逻辑卷为 $filesystem..."
    lv_path="/dev/$vg_name/$lv_name"
    
    # 检查文件系统工具
    if [ "$filesystem" == "xfs" ] && ! command -v mkfs.xfs &> /dev/null; then
        echo -e "${YELLOW}安装 xfsprogs...${NC}"
        apt install -y xfsprogs
    fi
    
    if [ "$filesystem" == "xfs" ]; then
        mkfs_cmd="mkfs.xfs -f"
    else
        mkfs_cmd="mkfs.ext4"
    fi
    
    if $mkfs_cmd "$lv_path"; then
        echo -e "  ${GREEN}✓ 格式化完成${NC}"
    else
        echo -e "  ${RED}✗ 格式化失败${NC}"
        return 1
    fi
    
    # 5. 创建挂载点
    echo "步骤 5: 创建挂载点..."
    if mkdir -p "$mount_point"; then
        echo -e "  ${GREEN}✓ 挂载点创建成功${NC}"
        
        # 设置合适的权限
        chown root:root "$mount_point"
        chmod 755 "$mount_point"
        echo -e "  ${GREEN}✓ 挂载点权限设置完成${NC}"
    else
        echo -e "  ${RED}✗ 挂载点创建失败${NC}"
        return 1
    fi
    
    # 6. 挂载
    echo "步骤 6: 挂载逻辑卷..."
    if mount "$lv_path" "$mount_point"; then
        echo -e "  ${GREEN}✓ 挂载成功${NC}"
    else
        echo -e "  ${RED}✗ 挂载失败${NC}"
        return 1
    fi
    
    # 7. 设置开机挂载
    echo "步骤 7: 配置开机自动挂载..."
    lv_uuid=$(blkid "$lv_path" -s UUID -o value)
    
    if [ -z "$lv_uuid" ]; then
        echo -e "  ${YELLOW}⚠ 无法获取 UUID,使用设备路径${NC}"
        fstab_entry="/dev/$vg_name/$lv_name $mount_point $filesystem defaults 0 0"
    else
        fstab_entry="UUID=$lv_uuid $mount_point $filesystem defaults 0 0"
    fi
    
    if ! grep -q "$mount_point" /etc/fstab; then
        echo "$fstab_entry" >> /etc/fstab
        echo -e "  ${GREEN}✓ fstab 配置完成${NC}"
    else
        echo -e "  ${YELLOW}⚠ fstab 中已存在该挂载点${NC}"
        # 询问是否替换现有配置
        read -r -p "是否替换现有配置?(y/N): " replace
        if [[ $replace =~ ^[Yy]$ ]]; then
            # 删除现有配置并添加新配置
            sed -i "\|$mount_point|d" /etc/fstab
            echo "$fstab_entry" >> /etc/fstab
            echo -e "  ${GREEN}✓ fstab 配置已更新${NC}"
        fi
    fi
    
    return 0
}

# 主程序
main() {
    # 用户配置
    vg_name="data_vg"
    lv_name="storage_lv"
    filesystem="ext4"
    
    # 检查权限
    check_root
    
    # 安装 LVM
    if ! install_lvm; then
        exit 1
    fi
    
    # 显示当前磁盘信息
    show_disk_info
    
    # 获取可用磁盘
    if ! get_available_disks; then
        exit 1
    fi
    
    # 选择磁盘
    if ! select_disks "${available_disks[@]}"; then
        exit 1
    fi
    
    # 输入挂载点
    if ! get_mount_point; then
        exit 1
    fi
    
    # 确认操作
    confirm_operation
    
    # 执行 LVM 配置
    if setup_lvm "${selected_disks[@]}"; then
        echo ""
        echo -e "${GREEN}=== LVM 配置完成 ===${NC}"
        echo "挂载点: $mount_point"
        echo "总容量: $(df -h $mount_point | awk 'NR==2 {print $2}')"
        echo "可用空间: $(df -h $mount_point | awk 'NR==2 {print $4}')"
        echo ""
        echo -e "${GREEN}验证命令:${NC}"
        echo "df -h $mount_point"
        echo "sudo pvs && sudo vgs && sudo lvs"
    else
        echo -e "${RED}=== LVM 配置失败 ===${NC}"
        exit 1
    fi
}

# 运行主程序
main "$@"

验证脚本 lvm_check.sh

复制代码
#!/bin/bash

echo "=== LVM 完整配置检验 ==="
echo ""

# 颜色定义
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# 函数:检查命令结果
check_cmd() {
    if $1 >/dev/null 2>&1; then
        echo -e "${GREEN}✓ $2${NC}"
        return 0
    else
        echo -e "${RED}✗ $2${NC}"
        return 1
    fi
}

# 1. 基础挂载检查
echo -e "${BLUE}1. 基础挂载检查${NC}"
if mountpoint -q /home/mnt 2>/dev/null; then
    echo -e "${GREEN}✓ /home/mnt 已正确挂载${NC}"
    df -h /home/mnt | tail -1
else
    echo -e "${RED}✗ /home/mnt 未挂载或挂载异常${NC}"
fi

# 2. LVM 组件检查
echo ""
echo -e "${BLUE}2. LVM 组件检查${NC}"
check_cmd "sudo pvs" "物理卷状态"
sudo pvs 2>/dev/null || true

echo ""
check_cmd "sudo vgs" "卷组状态"  
sudo vgs 2>/dev/null || true

echo ""
check_cmd "sudo lvs" "逻辑卷状态"
sudo lvs 2>/dev/null || true

# 3. 配置检查
echo ""
echo -e "${BLUE}3. 配置检查${NC}"
if grep -q "/home/mnt" /etc/fstab; then
    echo -e "${GREEN}✓ fstab 中找到 /home/mnt 配置${NC}"
    grep "/home/mnt" /etc/fstab
else
    echo -e "${RED}✗ fstab 中未找到 /home/mnt 配置${NC}"
fi

# 4. 功能测试
echo ""
echo -e "${BLUE}4. 功能测试${NC}"
# 写入测试
if sudo touch /home/mnt/test_file 2>/dev/null; then
    echo -e "${GREEN}✓ 写入权限正常${NC}"
    # 删除测试
    if sudo rm /home/mnt/test_file 2>/dev/null; then
        echo -e "${GREEN}✓ 删除权限正常${NC}"
    else
        echo -e "${RED}✗ 删除权限异常${NC}"
    fi
else
    echo -e "${RED}✗ 写入权限异常${NC}"
fi

# 5. 详细信息
echo ""
echo -e "${BLUE}5. 详细信息${NC}"
echo "设备映射:"
ls -la /dev/mapper/ | grep -E "(data_vg|storage_lv)" || echo "  未找到相关设备映射"

echo ""
echo "挂载详情:"
findmnt -n -o SOURCE,TARGET,FSTYPE,SIZE /home/mnt 2>/dev/null || echo "  未找到挂载信息"

# 6. 总结
echo ""
echo -e "${BLUE}6. 检验总结${NC}"
if mountpoint -q /home/mnt 2>/dev/null && \
   sudo lvs >/dev/null 2>&1 && \
   grep -q "/home/mnt" /etc/fstab 2>/dev/null; then
    echo -e "${GREEN}🎉 所有检查通过!LVM 配置完整且正常${NC}"
else
    echo -e "${YELLOW}⚠ 部分检查未通过,请检查上述错误信息${NC}"
fi

卸载脚本,执行选择3,lvm_unmount.sh

复制代码
#!/bin/bash

# LVM 卸载脚本(完全清理版)
set -e

echo "=== LVM 完全卸载脚本 ==="
echo ""

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# 检查 root 权限
if [ "$EUID" -ne 0 ]; then
    echo -e "${RED}错误: 请使用 sudo 运行此脚本${NC}"
    exit 1
fi

# 函数:强制清理 LVM 缓存和残留
force_clean_lvm_residue() {
    echo -e "${YELLOW}=== 强制清理 LVM 残留 ===${NC}"
    
    # 停止 LVM 相关服务
    echo "停止 LVM 服务..."
    systemctl stop lvm2-lvmetad.service 2>/dev/null || true
    systemctl stop lvm2-lvmpolld.service 2>/dev/null || true
    
    # 清理设备映射器
    echo "清理设备映射器..."
    dmsetup remove_all 2>/dev/null || true
    
    # 重新扫描 LVM
    echo "更新 LVM 缓存..."
    vgscan 2>/dev/null || true
    pvscan 2>/dev/null || true
    
    # 清理 LVM 缓存文件
    echo "清理 LVM 缓存文件..."
    rm -f /etc/lvm/cache/.cache 2>/dev/null || true
    
    # 重新加载设备映射器
    echo "重新加载设备映射器..."
    modprobe -r dm_mod 2>/dev/null || true
    modprobe dm_mod 2>/dev/null || true
    
    # udev 规则更新
    echo "更新 udev 规则..."
    udevadm trigger 2>/dev/null || true
    udevadm settle 2>/dev/null || true
    
    echo -e "${GREEN}✓ 强制清理完成${NC}"
}

# 函数:显示当前 LVM 状态
show_lvm_status() {
    echo -e "${GREEN}=== 当前 LVM 状态 ===${NC}"
    
    # 检查逻辑卷
    if sudo lvs --noheadings 2>/dev/null | grep -q .; then
        sudo lvs 2>/dev/null
        echo ""
    else
        echo "  无逻辑卷"
        echo ""
    fi
    
    # 检查卷组
    if sudo vgs --noheadings 2>/dev/null | grep -q .; then
        sudo vgs 2>/dev/null
        echo ""
    else
        echo "  无卷组"
        echo ""
    fi
    
    # 检查物理卷
    if sudo pvs --noheadings 2>/dev/null | grep -q .; then
        sudo pvs 2>/dev/null
        echo ""
    else
        echo "  无物理卷"
        echo ""
    fi
}

# 函数:显示磁盘状态
show_disk_status() {
    echo -e "${BLUE}=== 当前磁盘状态 ===${NC}"
    lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT | grep -E "(sda|sdb|sdc|NAME)" | grep -v loop
    echo ""
}

# 函数:选择要卸载的卷组
select_vg_to_remove() {
    echo -e "${YELLOW}可用的卷组:${NC}"
    local vg_list=($(vgs --noheadings -o vg_name 2>/dev/null | tr -d ' ' 2>/dev/null))
    
    if [ ${#vg_list[@]} -eq 0 ]; then
        echo -e "${RED}未找到任何卷组${NC}"
        return 1
    fi
    
    for i in "${!vg_list[@]}"; do
        echo "  $((i+1)). ${vg_list[$i]}"
    done
    
    echo ""
    read -r -p "请选择要卸载的卷组 (输入数字): " choice
    
    local index=$((choice-1))
    if [ $index -ge 0 ] && [ $index -lt ${#vg_list[@]} ]; then
        selected_vg="${vg_list[$index]}"
        echo -e "${GREEN}选择: $selected_vg${NC}"
        return 0
    else
        echo -e "${RED}错误选择${NC}"
        return 1
    fi
}

# 函数:获取挂载点信息
get_mount_info() {
    local vg_name=$1
    local lv_name=$(lvs --noheadings -o lv_name "$vg_name" 2>/dev/null | tr -d ' ' 2>/dev/null)
    
    if [ -n "$lv_name" ]; then
        lv_path="/dev/$vg_name/$lv_name"
        mount_point=$(findmnt -n -o TARGET "$lv_path" 2>/dev/null)
        
        if [ -n "$mount_point" ]; then
            echo -e "${YELLOW}找到挂载点: $mount_point${NC}"
        else
            echo -e "${YELLOW}逻辑卷未挂载${NC}"
        fi
    fi
}

# 函数:获取卷组使用的物理卷
get_vg_physical_volumes() {
    local vg_name=$1
    pv_list=($(pvs --noheadings -o pv_name -S "vg_name=$vg_name" 2>/dev/null | tr -d ' ' 2>/dev/null))
    if [ ${#pv_list[@]} -gt 0 ]; then
        echo "卷组 $vg_name 使用的物理卷: ${pv_list[*]}"
    else
        echo "未找到卷组 $vg_name 的物理卷"
    fi
}

# 函数:确认清理选项
confirm_cleanup_option() {
    echo ""
    echo -e "${YELLOW}=== 磁盘清理选项 ===${NC}"
    if [ ${#pv_list[@]} -gt 0 ]; then
        echo "物理卷: ${pv_list[*]}"
    fi
    echo ""
    echo "选择清理级别:"
    echo "  1. 基本清理 (仅移除 LVM 配置)"
    echo "  2. 深度清理 (移除 LVM 配置并清理磁盘元数据,推荐)"
    echo "  3. 强制清理 (完全清理所有 LVM 残留)"
    echo ""
    read -r -p "请选择清理级别 (1/2/3, 默认 2): " cleanup_level
    
    case "${cleanup_level:-2}" in
        1)
            cleanup_type="basic"
            echo -e "${GREEN}选择: 基本清理${NC}"
            ;;
        2)
            cleanup_type="deep"
            echo -e "${GREEN}选择: 深度清理${NC}"
            ;;
        3)
            cleanup_type="force"
            echo -e "${GREEN}选择: 强制清理${NC}"
            ;;
        *)
            cleanup_type="deep"
            echo -e "${GREEN}选择: 深度清理 (默认)${NC}"
            ;;
    esac
}

# 函数:确认卸载
confirm_unmount() {
    echo ""
    echo -e "${YELLOW}=== 卸载摘要 ===${NC}"
    echo "卷组: $selected_vg"
    echo "挂载点: ${mount_point:-未挂载}"
    if [ ${#pv_list[@]} -gt 0 ]; then
        echo "物理卷: ${pv_list[*]}"
    fi
    echo "清理级别: $cleanup_type"
    echo ""
    echo -e "${RED}警告: 这将删除所有数据并解除 LVM 配置!${NC}"
    echo ""
    
    read -r -p "确认卸载?(输入 'YES' 继续): " confirm
    if [ "$confirm" != "YES" ]; then
        echo -e "${YELLOW}操作已取消${NC}"
        exit 0
    fi
}

# 函数:执行磁盘清理
perform_disk_cleanup() {
    local cleanup_type=$1
    shift
    local pv_list=("$@")
    
    echo -e "${GREEN}=== 执行磁盘清理 ===${NC}"
    
    for pv in "${pv_list[@]}"; do
        if [ ! -e "$pv" ]; then
            echo -e "  ${YELLOW}⚠ 设备 $pv 不存在,跳过${NC}"
            continue
        fi
        
        echo "清理 $pv ..."
        case $cleanup_type in
            "basic")
                # 基本清理:只移除 LVM 配置
                pvremove -y "$pv" 2>/dev/null || true
                echo -e "  ${GREEN}✓ 基本清理完成${NC}"
                ;;
            "deep"|"force")
                # 深度清理:移除所有文件系统签名
                pvremove -y "$pv" 2>/dev/null || true
                wipefs -a "$pv" 2>/dev/null || true
                dd if=/dev/zero of="$pv" bs=1M count=10 status=none 2>/dev/null || true
                echo -e "  ${GREEN}✓ 深度清理完成${NC}"
                ;;
        esac
    done
    
    # 如果是强制清理,执行额外清理步骤
    if [ "$cleanup_type" = "force" ]; then
        force_clean_lvm_residue
    fi
}

# 函数:执行卸载
perform_unmount() {
    local vg_name=$1
    local mount_point=$2
    local cleanup_type=$3
    shift 3
    local pv_list=("$@")
    
    echo -e "${GREEN}=== 开始卸载 LVM ===${NC}"
    
    # 1. 卸载文件系统
    if [ -n "$mount_point" ] && mountpoint -q "$mount_point" 2>/dev/null; then
        echo "步骤 1: 卸载文件系统..."
        if umount "$mount_point"; then
            echo -e "  ${GREEN}✓ 卸载成功${NC}"
        else
            echo -e "  ${YELLOW}⚠ 尝试强制卸载${NC}"
            umount -l "$mount_point" 2>/dev/null || true
            echo -e "  ${GREEN}✓ 强制卸载完成${NC}"
        fi
    else
        echo -e "  ${GREEN}✓ 文件系统未挂载${NC}"
    fi
    
    # 2. 移除 fstab 配置
    echo "步骤 2: 移除 fstab 配置..."
    if [ -n "$mount_point" ] && grep -q "$mount_point" /etc/fstab 2>/dev/null; then
        # 备份 fstab
        cp /etc/fstab "/etc/fstab.backup.$(date +%Y%m%d_%H%M%S)" 2>/dev/null || true
        sed -i "\|$mount_point|d" /etc/fstab
        echo -e "  ${GREEN}✓ fstab 配置已移除 (已备份)${NC}"
    else
        echo -e "  ${GREEN}✓ fstab 中无相关配置${NC}"
    fi
    
    # 3. 删除挂载点目录(可选)
    if [ -n "$mount_point" ] && [ -d "$mount_point" ]; then
        echo "步骤 3: 清理挂载点..."
        read -r -p "是否删除挂载点目录 $mount_point?(y/N): " remove_dir
        if [[ $remove_dir =~ ^[Yy]$ ]]; then
            rm -rf "$mount_point"
            echo -e "  ${GREEN}✓ 目录已删除${NC}"
        else
            echo -e "  ${YELLOW}⚠ 保留目录 $mount_point${NC}"
        fi
    fi
    
    # 4. 删除逻辑卷
    echo "步骤 4: 删除逻辑卷..."
    local lv_name=$(lvs --noheadings -o lv_name "$vg_name" 2>/dev/null | tr -d ' ' 2>/dev/null)
    if [ -n "$lv_name" ]; then
        if lvremove -f "$vg_name/$lv_name" 2>/dev/null; then
            echo -e "  ${GREEN}✓ 逻辑卷已删除${NC}"
        else
            echo -e "  ${YELLOW}⚠ 逻辑卷删除失败,尝试强制清理${NC}"
        fi
    else
        echo -e "  ${GREEN}✓ 无逻辑卷需要删除${NC}"
    fi
    
    # 5. 删除卷组
    echo "步骤 5: 删除卷组..."
    if vgremove "$vg_name" 2>/dev/null; then
        echo -e "  ${GREEN}✓ 卷组已删除${NC}"
    else
        echo -e "  ${YELLOW}⚠ 卷组删除失败,尝试强制清理${NC}"
    fi
    
    # 6. 执行磁盘清理
    if [ ${#pv_list[@]} -gt 0 ]; then
        perform_disk_cleanup "$cleanup_type" "${pv_list[@]}"
    fi
    
    # 7. 强制清理 LVM 残留(如果是强制清理模式)
    if [ "$cleanup_type" = "force" ]; then
        force_clean_lvm_residue
    fi
    
    return 0
}

# 函数:验证卸载结果
verify_unmount() {
    echo ""
    echo -e "${GREEN}=== 验证卸载结果 ===${NC}"
    
    local errors=0
    
    # 检查 LVM 配置
    echo "检查 LVM 配置..."
    if sudo lvs --noheadings 2>/dev/null | grep -q .; then
        echo -e "  ${RED}✗ 仍有逻辑卷存在${NC}"
        ((errors++))
    else
        echo -e "  ${GREEN}✓ 无逻辑卷${NC}"
    fi
    
    if sudo vgs --noheadings 2>/dev/null | grep -q .; then
        echo -e "  ${RED}✗ 仍有卷组存在${NC}"
        ((errors++))
    else
        echo -e "  ${GREEN}✓ 无卷组${NC}"
    fi
    
    if sudo pvs --noheadings 2>/dev/null | grep -q .; then
        echo -e "  ${RED}✗ 仍有物理卷存在${NC}"
        ((errors++))
    else
        echo -e "  ${GREEN}✓ 无物理卷${NC}"
    fi
    
    # 检查磁盘状态
    echo ""
    echo -e "${BLUE}=== 最终磁盘状态 ===${NC}"
    lsblk -o NAME,SIZE,FSTYPE,MOUNTPOINT | grep -E "(sda|sdb|sdc|NAME)"
    
    # 检查是否有 LVM2_member 残留
    if lsblk -o FSTYPE | grep -q "LVM2_member"; then
        echo -e "  ${YELLOW}⚠ 磁盘上仍有 LVM 元数据残留${NC}"
        ((errors++))
    else
        echo -e "  ${GREEN}✓ 磁盘元数据已清理${NC}"
    fi
    
    if [ $errors -eq 0 ]; then
        echo -e "${GREEN}✓ 所有 LVM 配置已完全清理${NC}"
    else
        echo -e "${YELLOW}⚠ 仍有 $errors 个问题需要处理${NC}"
    fi
    
    return $errors
}

# 主程序
main() {
    # 显示当前状态
    show_lvm_status
    show_disk_status
    
    # 选择要卸载的卷组
    if ! select_vg_to_remove; then
        echo -e "${YELLOW}未找到可卸载的卷组,检查磁盘残留...${NC}"
        # 检查是否有 LVM 元数据的磁盘
        lvm_disks=($(lsblk -o NAME,FSTYPE -n 2>/dev/null | grep "LVM2_member" | awk '{print $1}' 2>/dev/null))
        if [ ${#lvm_disks[@]} -gt 0 ]; then
            echo "发现带有 LVM 元数据的磁盘: ${lvm_disks[*]}"
            pv_list=()
            for disk in "${lvm_disks[@]}"; do
                pv_list+=("/dev/$disk")
            done
            confirm_cleanup_option
            perform_disk_cleanup "$cleanup_type" "${pv_list[@]}"
            verify_unmount
        else
            echo -e "${GREEN}系统已清理干净,无需操作${NC}"
        fi
        exit 0
    fi
    
    # 获取挂载点信息
    get_mount_info "$selected_vg"
    
    # 获取物理卷信息
    get_vg_physical_volumes "$selected_vg"
    
    # 确认清理选项
    confirm_cleanup_option
    
    # 确认操作
    confirm_unmount
    
    # 执行卸载
    if perform_unmount "$selected_vg" "$mount_point" "$cleanup_type" "${pv_list[@]}"; then
        echo ""
        echo -e "${GREEN}=== LVM 卸载完成 ===${NC}"
        echo "卷组 $selected_vg 已完全移除"
        echo "磁盘清理级别: $cleanup_type"
        echo "物理磁盘现在可以安全移除或重新使用"
    else
        echo -e "${RED}=== LVM 卸载失败 ===${NC}"
        exit 1
    fi
    
    # 验证结果
    verify_unmount
    
    # 如果仍有问题,建议重启
    if [ $? -ne 0 ]; then
        echo ""
        echo -e "${YELLOW}建议重启系统以确保完全清理 LVM 配置${NC}"
        read -r -p "是否立即重启系统?(y/N): " reboot_choice
        if [[ $reboot_choice =~ ^[Yy]$ ]]; then
            reboot
        fi
    fi
}

# 运行主程序
main "$@"

扩展 LVM 存储空间脚本lvm_extend.sh

又给服务器增加了一块盘,也要挂载到那个目录,保持原来挂载目录下的内容不要变

复制代码
#!/bin/bash

# LVM 扩展脚本
set -e

echo "=== LVM 存储扩展脚本 ==="
echo ""

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# 检查 root 权限
if [ "$EUID" -ne 0 ]; then
    echo -e "${RED}错误: 请使用 sudo 运行此脚本${NC}"
    exit 1
fi

# 函数:显示当前状态
show_current_status() {
    echo -e "${GREEN}=== 当前 LVM 状态 ===${NC}"
    sudo lvs && echo ""
    sudo vgs && echo ""
    sudo pvs && echo ""
    
    echo -e "${BLUE}=== 当前磁盘状态 ===${NC}"
    lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT | grep -v loop
    echo ""
    
    echo -e "${YELLOW}=== 当前挂载状态 ===${NC}"
    df -h /home/mnt
    echo ""
}

# 函数:选择要扩展的卷组
select_vg_to_extend() {
    echo -e "${YELLOW}可用的卷组:${NC}"
    local vg_list=($(vgs --noheadings -o vg_name 2>/dev/null | tr -d ' '))
    
    if [ ${#vg_list[@]} -eq 0 ]; then
        echo -e "${RED}未找到任何卷组${NC}"
        exit 1
    fi
    
    for i in "${!vg_list[@]}"; do
        vg_size=$(vgs --noheadings -o vg_size "$vg_name" 2>/dev/null)
        echo "  $((i+1)). ${vg_list[$i]} ($vg_size)"
    done
    
    echo ""
    read -r -p "请选择要扩展的卷组 (输入数字): " choice
    
    local index=$((choice-1))
    if [ $index -ge 0 ] && [ $index -lt ${#vg_list[@]} ]; then
        selected_vg="${vg_list[$index]}"
        echo -e "${GREEN}选择: $selected_vg${NC}"
    else
        echo -e "${RED}错误选择${NC}"
        exit 1
    fi
}

# 函数:选择新磁盘
select_new_disk() {
    echo -e "${YELLOW}可用的新磁盘:${NC}"
    
    # 获取所有未使用的磁盘
    available_disks=()
    while IFS= read -r line; do
        disk_name=$(echo "$line" | awk '{print $1}')
        disk_type=$(echo "$line" | awk '{print $3}')
        mountpoint=$(echo "$line" | awk '{print $4}')
        fstype=$(echo "$line" | awk '{print $5}')
        
        # 检查是否是磁盘类型且未挂载、无文件系统、不是 LVM 成员
        if [ "$disk_type" == "disk" ] && [ -z "$mountpoint" ] && [ -z "$fstype" ] && \
           [ "$disk_name" != "NAME" ] && ! lsblk "/dev/$disk_name" | grep -q "/$"; then
            # 检查是否已经在 LVM 中
            if ! pvs | grep -q "/dev/$disk_name"; then
                available_disks+=("$disk_name")
            fi
        fi
    done < <(lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE | tail -n +2)
    
    if [ ${#available_disks[@]} -eq 0 ]; then
        echo -e "${RED}未找到可用的新磁盘!${NC}"
        exit 1
    fi
    
    for i in "${!available_disks[@]}"; do
        disk_size=$(lsblk -b -d -o NAME,SIZE "/dev/${available_disks[$i]}" | grep "^${available_disks[$i]}" | awk '{print $2}')
        size_gb=$(echo "scale=2; $disk_size/1024/1024/1024" | bc)
        echo "  $((i+1)). /dev/${available_disks[$i]} (${size_gb} GB)"
    done
    
    echo ""
    read -r -p "请选择要添加的新磁盘 (输入数字): " choice
    
    local index=$((choice-1))
    if [ $index -ge 0 ] && [ $index -lt ${#available_disks[@]} ]; then
        new_disk="/dev/${available_disks[$index]}"
        echo -e "${GREEN}选择: $new_disk${NC}"
    else
        echo -e "${RED}错误选择${NC}"
        exit 1
    fi
}

# 函数:确认扩展操作
confirm_extend() {
    echo ""
    echo -e "${YELLOW}=== 扩展摘要 ===${NC}"
    echo "卷组: $selected_vg"
    echo "新磁盘: $new_disk"
    echo "挂载点: /home/mnt"
    echo ""
    echo -e "${GREEN}优势: 现有数据将完全保留,无需迁移!${NC}"
    echo ""
    
    read -r -p "确认扩展操作?(输入 'YES' 继续): " confirm
    if [ "$confirm" != "YES" ]; then
        echo -e "${YELLOW}操作已取消${NC}"
        exit 0
    fi
}

# 函数:执行扩展
perform_extend() {
    local vg_name=$1
    local new_disk=$2
    
    echo -e "${GREEN}=== 开始扩展 LVM ===${NC}"
    
    # 1. 创建新物理卷
    echo "步骤 1: 创建新物理卷..."
    if pvcreate "$new_disk"; then
        echo -e "  ${GREEN}✓ 物理卷创建成功${NC}"
    else
        echo -e "  ${RED}✗ 物理卷创建失败${NC}"
        return 1
    fi
    
    # 2. 扩展卷组
    echo "步骤 2: 扩展卷组..."
    if vgextend "$vg_name" "$new_disk"; then
        echo -e "  ${GREEN}✓ 卷组扩展成功${NC}"
    else
        echo -e "  ${RED}✗ 卷组扩展失败${NC}"
        return 1
    fi
    
    # 3. 扩展逻辑卷(使用全部新空间)
    echo "步骤 3: 扩展逻辑卷..."
    local lv_name=$(lvs --noheadings -o lv_name "$vg_name" | tr -d ' ')
    if lvextend -l +100%FREE "/dev/$vg_name/$lv_name"; then
        echo -e "  ${GREEN}✓ 逻辑卷扩展成功${NC}"
    else
        echo -e "  ${RED}✗ 逻辑卷扩展失败${NC}"
        return 1
    fi
    
    # 4. 扩展文件系统(在线扩展,无需卸载)
    echo "步骤 4: 扩展文件系统..."
    local fs_type=$(df -T /home/mnt | awk 'NR==2 {print $2}')
    
    if [ "$fs_type" == "ext4" ]; then
        # 对于 ext4 文件系统
        if resize2fs "/dev/$vg_name/$lv_name"; then
            echo -e "  ${GREEN}✓ 文件系统扩展成功${NC}"
        else
            echo -e "  ${RED}✗ 文件系统扩展失败${NC}"
            return 1
        fi
    elif [ "$fs_type" == "xfs" ]; then
        # 对于 xfs 文件系统
        if xfs_growfs "/home/mnt"; then
            echo -e "  ${GREEN}✓ 文件系统扩展成功${NC}"
        else
            echo -e "  ${RED}✗ 文件系统扩展失败${NC}"
            return 1
        fi
    else
        echo -e "  ${YELLOW}⚠ 未知文件系统类型: $fs_type,请手动扩展${NC}"
    fi
    
    return 0
}

# 主程序
main() {
    # 显示当前状态
    show_current_status
    
    # 选择要扩展的卷组
    select_vg_to_extend
    
    # 选择新磁盘
    select_new_disk
    
    # 确认操作
    confirm_extend
    
    # 执行扩展
    if perform_extend "$selected_vg" "$new_disk"; then
        echo ""
        echo -e "${GREEN}=== LVM 扩展完成 ===${NC}"
        echo "卷组 $selected_vg 已成功扩展"
        echo "新磁盘 $new_disk 已添加到存储池"
        echo ""
        echo -e "${BLUE}扩展后的状态:${NC}"
        df -h /home/mnt
        echo ""
        sudo vgs "$selected_vg"
    else
        echo -e "${RED}=== LVM 扩展失败 ===${NC}"
        exit 1
    fi
}

# 运行主程序
main "$@"
相关推荐
r***F2621 小时前
【漏洞复现】CVE-2019-11043(PHP远程代码执行漏洞)信息安全论文_含漏洞复现完整过程_含Linux环境go语言编译环境安装
linux·golang·php
smaller_maple2 小时前
linux问题记录1
linux·运维·服务器
v***8573 小时前
Ubuntu介绍、与centos的区别、基于VMware安装Ubuntu Server 22.04、配置远程连接、安装jdk+Tomcat
java·ubuntu·centos
报错小能手3 小时前
讲讲libevent底层机制
linux·服务器
阿星智力囊4 小时前
Thinkphp6+nginx环境报错信息不显示,接口直接报500和CORS跨域(错误的引导方向),真坑啊
运维·nginx·php·thinkphp6
代码AC不AC6 小时前
【Linux】计算机的基石:从冯·诺依曼体系结构到操作系统管理
linux·操作系统·冯诺依曼体系结构
大柏怎么被偷了6 小时前
【Linux】进程等待
linux·运维·服务器
云和数据.ChenGuang7 小时前
运维面试题之oracle和mysql单表最大容量
运维·mysql·oracle
偶像你挑的噻7 小时前
12-Linux驱动开发- SPI子系统
linux·驱动开发·stm32·嵌入式硬件