发布作者: yi
百度收录: 正在检测是否收录...
作品采用: 《 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 》许可协议授权
#!/data/data/com.termux/files/usr/bin/bash
# QCOW2镜像管理器
# ====================== #
# ANSI标准色编码
# ====================== #
RED='\033[1;31m' # 错误/警告
GREEN='\033[1;32m' # 成功
YELLOW='\033[1;33m' # 提示
BLUE='\033[1;34m' # 标题
PURPLE='\033[1;35m' # 高亮
CYAN='\033[1;36m' # 数据
NC='\033[0m' # 重置颜色
# 安全分隔线
HR_LINE="================================================================"
# ====================== #
# 全局配置
# ====================== #
selected_image=""
EFI_FIRMWARE="./QEMU_EFI.fd"
EXPANDED_FIRMWARE="./flash.img"
QEMU_BINARY="./qemu-system-aarch64"
KEYMAP_PATH="/usr/share/qemu/keymaps/en-us"
DEFAULT_IMAGE_URL="https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-generic-arm64.qcow2" # 官方ARM64镜像[7](@ref)
# ====================== #
# 依赖检查
# ====================== #
check_dependencies() {
# 新增wget依赖检查
if ! command -v wget &> /dev/null; then
echo -e "${YELLOW}▶ 安装wget下载工具...${NC}"
pkg install wget -y
fi
if ! command -v qemu-img &> /dev/null; then
echo -e "${RED}✗ 错误: 未找到 qemu-img 命令!${NC}" >&2
echo -e "${YELLOW}请执行: pkg install qemu-utils${NC}" >&2
exit 1
fi
if [ ! -f "$QEMU_BINARY" ]; then
echo -e "${YELLOW}⚠ 警告: QEMU可执行文件未找到${NC}"
echo -e "${CYAN}请确保 ${PURPLE}$QEMU_BINARY ${CYAN}存在于当前目录${NC}"
fi
if [ ! -f "$KEYMAP_PATH" ]; then
echo -e "${YELLOW}⚠ 警告: 键盘映射文件缺失 (路径: $KEYMAP_PATH)${NC}"
echo -e "${YELLOW} 这可能导致启动时出现键盘映射警告${NC}"
fi
}
# ====================== #
# 镜像自动下载功能
# ====================== #
download_default_image() {
local image_name="debian-12-arm64.qcow2"
echo -e "\n${YELLOW}▶ 未找到可用镜像,开始下载Debian 12 ARM64官方镜像...${NC}"
echo -e "${CYAN}镜像源: ${PURPLE}$DEFAULT_IMAGE_URL${NC}"
echo -e "${YELLOW}此过程可能需要5-10分钟,请保持网络连接...${NC}"
# 显示下载进度条
wget --progress=dot:giga "$DEFAULT_IMAGE_URL" -O "$image_name"
if [ $? -eq 0 ]; then
echo -e "\n${GREEN}✓ 镜像下载成功: ${CYAN}$image_name${NC}"
echo -e "${YELLOW}镜像大小: ${CYAN}$(du -h $image_name | awk '{print $1}')${NC}"
return 0
else
echo -e "${RED}✗ 镜像下载失败!${NC}"
echo -e "${YELLOW}可能原因:"
echo " 1. 网络连接不稳定"
echo " 2. 磁盘空间不足"
echo " 3. 镜像源不可用"
return 1
fi
}
# ====================== #
# 镜像选择器
# ====================== #
select_qcow2_image() {
local images=()
echo -e "\n${BLUE}=== 发现镜像文件 ===${NC}"
echo -e "${HR_LINE}"
while IFS= read -r file; do
images+=("$file")
done < <(find . -maxdepth 1 -type f -name '*.qcow2' | sed 's|^\./||')
if [ ${#images[@]} -eq 0 ]; then
echo -e "${YELLOW}⚠ 未找到.qcow2镜像文件!${NC}"
# 自动下载默认镜像
download_default_image
if [ $? -ne 0 ]; then
return 1
fi
# 重新扫描下载的镜像
while IFS= read -r file; do
images+=("$file")
done < <(find . -maxdepth 1 -type f -name '*.qcow2' | sed 's|^\./||')
if [ ${#images[@]} -eq 0 ]; then
echo -e "${RED}✗ 下载后仍未找到镜像文件!${NC}"
return 1
fi
fi
for i in "${!images[@]}"; do
printf "${PURPLE}%2d) ${CYAN}%s${NC}\n" $((i+1)) "${images[$i]}"
done
read -p "$(echo -e "${BLUE}➤ 请选择镜像 [1-${#images[@]}]:${NC} ")" choice
if [[ $choice =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le ${#images[@]} ]; then
selected_image="${images[$((choice-1))]}"
echo -e "${GREEN}✓ 已选择: ${CYAN}$selected_image${NC}"
return 0
else
echo -e "${RED}✗ 无效选择!${NC}"
return 1
fi
}
# ====================== #
# 准备UEFI固件
# ====================== #
prepare_firmware() {
if [ -f "$EXPANDED_FIRMWARE" ]; then
echo -e "${GREEN}✓ 使用已存在的扩展固件镜像${NC}"
return 0
fi
echo -e "${YELLOW}▶ 创建扩展固件镜像...${NC}"
if [ ! -f "$EFI_FIRMWARE" ]; then
echo -e "${YELLOW}│ 下载UEFI固件...${NC}"
wget -q https://releases.linaro.org/components/kernel/uefi-linaro/latest/release/qemu64/QEMU_EFI.fd -O "$EFI_FIRMWARE"
fi
# 创建64MB镜像
dd if=/dev/zero of="$EXPANDED_FIRMWARE" bs=1M count=64 status=none
dd if="$EFI_FIRMWARE" of="$EXPANDED_FIRMWARE" conv=notrunc status=none
}
# ====================== #
# 快照管理功能
# ====================== #
create_snapshot() {
select_qcow2_image || return 1
local image="$selected_image"
snapshot_name="snap_$(date +"%Y%m%d_%H%M%S")"
read -p "$(echo -e "${YELLOW}➤ 输入快照描述 ${CYAN}(可选):${NC} ")" description
[ -n "$description" ] && snapshot_name+="_$description"
qemu-img snapshot -c "$snapshot_name" "$image"
if [ $? -eq 0 ]; then
echo -e "${GREEN}✓ 时间点快照创建成功: ${CYAN}$snapshot_name${NC}"
else
echo -e "${RED}✗ 创建失败!${YELLOW}可能原因:${NC}"
echo " 1. 镜像非qcow2格式"
echo " 2. 磁盘空间不足"
fi
}
list_snapshots() {
select_qcow2_image || return 1
local image="$selected_image"
echo -e "\n${BLUE}=== ${CYAN}$image 的快照链 ===${NC}"
echo -e "${HR_LINE}"
snap_list=$(qemu-img snapshot -l "$image" 2>/dev/null)
if [ -z "$snap_list" ]; then
echo -e "${YELLOW}⚠ 未找到快照!${NC}"
return
fi
echo -e "${PURPLE}序号 ID 标签 创建时间${NC}"
counter=1
while IFS= read -r line; do
if [[ $line =~ ^[0-9] ]]; then
snap_id=$(echo "$line" | awk '{print $1}')
snap_tag=$(echo "$line" | awk '{print $2}')
snap_date=$(echo "$line" | awk '{print $3, $4}')
printf "${CYAN}%-4s %-4s %-20s %s${NC}\n" "$counter" "$snap_id" "$snap_tag" "$snap_date"
((counter++))
fi
done <<< "$snap_list"
echo -e "\n${BLUE}=== 操作选项 ===${NC}"
echo -e "${YELLOW}1) 回滚到快照${NC}"
echo -e "${YELLOW}2) 删除快照${NC}"
echo -e "${YELLOW}3) 返回主菜单${NC}"
read -p "$(echo -e "${BLUE}➤ 请选择:${NC} ")" action
case $action in
1) revert_snapshot "$image" ;;
2) delete_snapshot "$image" ;;
*) return ;;
esac
}
revert_snapshot() {
local image="$1"
read -p "$(echo -e "${YELLOW}➤ 输入要回滚的快照序号:${NC} ")" snap_num
if ! [[ $snap_num =~ ^[0-9]+$ ]]; then
echo -e "${RED}✗ 错误: 请输入数字序号!${NC}" >&2
return 1
fi
snap_tag=$(qemu-img snapshot -l "$image" 2>/dev/null | awk "NR==$((snap_num+1)) {print \$2}")
if [ -z "$snap_tag" ]; then
echo -e "${RED}✗ 错误: 无效的快照序号!${NC}" >&2
return 1
fi
if lsof "$image" &> /dev/null; then
echo -e "${RED}✗ 错误: 镜像正在使用中,请先停止虚拟机!${NC}" >&2
return 1
fi
qemu-img snapshot -a "$snap_tag" "$image"
if [ $? -eq 0 ]; then
echo -e "${GREEN}✓ 已回滚到快照: ${CYAN}$snap_tag${NC}"
else
echo -e "${RED}✗ 回滚失败!${YELLOW}可能原因:${NC}"
echo " 1. 快照元数据损坏"
echo " 2. 磁盘权限不足"
fi
}
delete_snapshot() {
local image="$1"
echo -e "\n${RED}=== ${CYAN}$image 的快照管理 ===${NC}"
echo -e "${HR_LINE}"
snap_list=$(qemu-img snapshot -l "$image" 2>/dev/null)
if [ -z "$snap_list" ]; then
echo -e "${YELLOW}⚠ 此镜像没有快照!${NC}" >&2
return 1
fi
echo -e "${PURPLE}序号 ID 标签 创建时间${NC}"
counter=1
declare -a snap_tags
while IFS= read -r line; do
if [[ $line =~ ^[0-9] ]]; then
snap_id=$(echo "$line" | awk '{print $1}')
snap_tag=$(echo "$line" | awk '{print $2}')
snap_date=$(echo "$line" | awk '{print $3, $4}')
printf "${CYAN}%-4s %-4s %-20s %s${NC}\n" "$counter" "$snap_id" "$snap_tag" "$snap_date"
snap_tags[$counter]="$snap_tag"
((counter++))
fi
done <<< "$snap_list"
if [ $counter -eq 1 ]; then
echo -e "${YELLOW}⚠ 未找到有效快照!${NC}" >&2
return 1
fi
read -p "$(echo -e "${YELLOW}➤ 输入要删除的快照序号:${NC} ")" snap_num
if ! [[ $snap_num =~ ^[0-9]+$ ]] || [ "$snap_num" -lt 1 ] || [ "$snap_num" -ge $counter ]; then
echo -e "${RED}✗ 错误: 无效序号!${NC}" >&2
return 1
fi
snap_tag="${snap_tags[$snap_num]}"
if [ -z "$snap_tag" ]; then
echo -e "${RED}✗ 错误: 无法解析快照标签!${NC}" >&2
return 1
fi
# 危险操作二次确认
echo -e "\n${RED}=== 危险操作警告 ===${NC}"
echo -e "${RED}您将要删除快照: ${PURPLE}$snap_tag${NC}"
read -p "$(echo -e "${RED}❗ 确认删除? (输入${CYAN}CONFIRM${RED}全大写确认):${NC} ")" confirm
[[ "$confirm" != "CONFIRM" ]] && return
qemu-img snapshot -d "$snap_tag" "$image"
if [ $? -eq 0 ]; then
echo -e "${GREEN}✓ 快照已删除: ${CYAN}$snap_tag${NC}"
else
echo -e "${RED}✗ 删除失败!${YELLOW}可能原因:${NC}"
echo " 1. 快照被虚拟机锁定"
echo " 2. 磁盘空间不足"
echo " 3. 快照链损坏"
fi
}
# ====================== #
# 镜像扩容
# ====================== #
resize_image() {
select_qcow2_image || return 1
local image="$selected_image"
read -p "$(echo -e "${YELLOW}➤ 输入扩容大小 ${CYAN}(示例: +10G 或 20G):${NC} ")" size
qemu-img resize "$image" "$size"
if [ $? -eq 0 ]; then
echo -e "\n${GREEN}✓ 镜像已扩容!${YELLOW}请在虚拟机内执行:${NC}"
echo -e "${CYAN} 1. 扩展分区: ${PURPLE}growpart /dev/sda 1"
echo -e "${CYAN} 2. 调整文件系统: ${PURPLE}resize2fs /dev/sda1${NC}"
else
echo -e "${RED}✗ 扩容失败!${YELLOW}可能原因:${NC}"
echo " 1. 磁盘空间不足"
echo " 2. 镜像格式限制"
fi
}
# ====================== #
# 启动虚拟机
# ====================== #
start_vm() {
select_qcow2_image || return 1
local image="$selected_image"
prepare_firmware
# 严格验证文件存在性
if [ ! -f "$EXPANDED_FIRMWARE" ]; then
echo -e "${RED}✗ 错误: 固件文件 $EXPANDED_FIRMWARE 不存在!${NC}"
return 1
fi
if [ ! -f "$image" ]; then
echo -e "${RED}✗ 错误: 镜像文件 $image 不存在!${NC}"
return 1
fi
# 显示配置信息
echo -e "\n${BLUE}=== 启动虚拟机 ===${NC}"
echo -e "${HR_LINE}"
echo -e "${GREEN}镜像: ${CYAN}$image${NC}"
echo -e "${GREEN}固件: ${CYAN}$EXPANDED_FIRMWARE${NC}"
echo -e "${YELLOW}端口映射:${NC}"
echo -e " ${CYAN}SSH: 22 → 22${NC}"
echo -e " ${CYAN}HTTP: 80 → 80${NC}"
echo -e " ${CYAN}HTTPS: 443 → 443${NC}"
echo -e "${HR_LINE}"
echo -e "${YELLOW}按 Ctrl+C 停止虚拟机${NC}\n"
"$QEMU_BINARY" \
-global virtio-blk.device_activation_delay_ms=1500 \
-machine virt,accel=kvm,gic-version=3 \
-m 4096 \
-cpu host \
-smp 4 \
-drive if=pflash,format=raw,file="$EXPANDED_FIRMWARE" \
-drive file="$image",if=virtio,format=qcow2 \
-netdev user,id=net0,hostfwd=tcp::22-:22,hostfwd=tcp::80-:80,hostfwd=tcp::443-:443,hostfwd=tcp::9876-:9876 \
-device virtio-net-device,netdev=net0 \
-nographic \
-serial mon:stdio
}
# ====================== #
# 主菜单
# ====================== #
main_menu() {
clear
echo -e "${BLUE}${HR_LINE}"
echo -e " QCOW2 镜像管理器 "
echo -e "${HR_LINE}${NC}"
echo -e "${GREEN}1) 创建内部快照${NC}"
echo -e "${GREEN}2) 列出/管理快照${NC}"
echo -e "${GREEN}3) 镜像扩容${NC}"
echo -e "${GREEN}4) 启动虚拟机${NC}"
echo -e "${RED}5) 退出${NC}"
echo -e "${HR_LINE}"
read -p "$(echo -e "${BLUE}➤ 选择操作 [1-5]:${NC} ")" choice
case $choice in
1) create_snapshot ;;
2) list_snapshots ;;
3) resize_image ;;
4) start_vm ;;
5) exit 0 ;;
*) echo -e "${RED}✗ 无效选项!${NC}"; sleep 1 ;;
esac
read -p "$(echo -e "${YELLOW}⏎ 按回车继续...${NC}")"
main_menu
}
check_dependencies
main_menu
注意:脚本自动开启kvm虚拟化,如果手机不支持kvm内核手动删除virt,accel=kvm,参数
主要功能:
镜像自动下载功能
镜像管理核心功能
快照管理系统
镜像扩容功能
虚拟机启动功能
增强特性
安全机制
维护功能
—— 评论区 ——