代码结构优化

This commit is contained in:
Super Manito
2025-07-18 08:26:53 +08:00
parent 4db5087208
commit 14cfbc727d
2 changed files with 305 additions and 383 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,12 @@
#!/bin/bash #!/bin/bash
## Author: SuperManito ## Author: SuperManito
## Modified: 2025-07-04 ## Modified: 2025-07-18
## License: MIT ## License: MIT
## GitHub: https://github.com/SuperManito/LinuxMirrors ## GitHub: https://github.com/SuperManito/LinuxMirrors
## Website: https://linuxmirrors.cn ## Website: https://linuxmirrors.cn
## Docker CE 软件源列表 ## Docker CE 软件源列表
# 格式:"软件源名称@软件源地址" # 格式:"名称@地址"
mirror_list_docker_ce=( mirror_list_docker_ce=(
"阿里云@mirrors.aliyun.com/docker-ce" "阿里云@mirrors.aliyun.com/docker-ce"
"腾讯云@mirrors.tencent.com/docker-ce" "腾讯云@mirrors.tencent.com/docker-ce"
@@ -24,7 +24,7 @@ mirror_list_docker_ce=(
) )
## Docker Registry 仓库列表 ## Docker Registry 仓库列表
# 格式:"软件源名称@软件源地址" # 格式:"名称@地址"
mirror_list_registry=( mirror_list_registry=(
"毫秒镜像(推荐)@docker.1ms.run" "毫秒镜像(推荐)@docker.1ms.run"
"Docker Proxy@dockerproxy.net" "Docker Proxy@dockerproxy.net"
@@ -90,6 +90,7 @@ SYSTEM_CENTOS_STREAM="CentOS Stream"
SYSTEM_ROCKY="Rocky" SYSTEM_ROCKY="Rocky"
SYSTEM_ALMALINUX="AlmaLinux" SYSTEM_ALMALINUX="AlmaLinux"
SYSTEM_FEDORA="Fedora" SYSTEM_FEDORA="Fedora"
SYSTEM_ORACLE="Oracle Linux"
SYSTEM_OPENCLOUDOS="OpenCloudOS" SYSTEM_OPENCLOUDOS="OpenCloudOS"
SYSTEM_OPENCLOUDOS_STREAM="OpenCloudOS Stream" SYSTEM_OPENCLOUDOS_STREAM="OpenCloudOS Stream"
SYSTEM_OPENEULER="openEuler" SYSTEM_OPENEULER="openEuler"
@@ -166,9 +167,7 @@ function main() {
run_end run_end
} }
## 处理命令选项
function handle_command_options() { function handle_command_options() {
## 命令帮助
function output_command_help() { function output_command_help() {
echo -e " echo -e "
命令选项(名称/含义/值) 命令选项(名称/含义/值)
@@ -198,13 +197,13 @@ function handle_command_options() {
if [ "$2" ]; then if [ "$2" ]; then
echo "$2" | grep -Eq "\(|\)|\[|\]|\{|\}" echo "$2" | grep -Eq "\(|\)|\[|\]|\{|\}"
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
output_error "命令选项 ${BLUE}$2${PLAIN} 无效,请在该选项后指定有效的地址" command_error "$2" "有效的地址"
else else
SOURCE="$(echo "$2" | sed -e 's,^http[s]\?://,,g' -e 's,/$,,')" SOURCE="$(echo "$2" | sed -e 's,^http[s]\?://,,g' -e 's,/$,,')"
shift shift
fi fi
else else
output_error "命令选项 ${BLUE}$1${PLAIN} 无效,请在该选项后指定软件源地址" command_error "$1" "软件源地址"
fi fi
;; ;;
## 指定 Docker Registry 仓库地址 ## 指定 Docker Registry 仓库地址
@@ -212,13 +211,13 @@ function handle_command_options() {
if [ "$2" ]; then if [ "$2" ]; then
echo "$2" | grep -Eq "\(|\)|\[|\]|\{|\}" echo "$2" | grep -Eq "\(|\)|\[|\]|\{|\}"
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
output_error "命令选项 ${BLUE}$2${PLAIN} 无效,请在该选项后指定有效的地址" command_error "$2" "有效的地址"
else else
SOURCE_REGISTRY="$(echo "$2" | sed -e 's,^http[s]\?://,,g' -e 's,/$,,')" SOURCE_REGISTRY="$(echo "$2" | sed -e 's,^http[s]\?://,,g' -e 's,/$,,')"
shift shift
fi fi
else else
output_error "命令选项 ${BLUE}$1${PLAIN} 无效,请在该选项后指定镜像仓库地址" command_error "$1" "镜像仓库地址"
fi fi
;; ;;
## 指定 Docker CE 软件源仓库 ## 指定 Docker CE 软件源仓库
@@ -227,7 +226,7 @@ function handle_command_options() {
SOURCE_BRANCH="$2" SOURCE_BRANCH="$2"
shift shift
else else
output_error "命令选项 ${BLUE}$1${PLAIN} 无效,请在该选项后指定软件源仓库" command_error "$1" "软件源仓库"
fi fi
;; ;;
## 指定 Debian 版本代号 ## 指定 Debian 版本代号
@@ -236,7 +235,7 @@ function handle_command_options() {
DEBIAN_CODENAME="$2" DEBIAN_CODENAME="$2"
shift shift
else else
output_error "命令选项 ${BLUE}$1${PLAIN} 无效,请在该选项后指定版本代号" command_error "$1" "版本代号"
fi fi
;; ;;
## 指定 Docker Engine 安装版本 ## 指定 Docker Engine 安装版本
@@ -247,10 +246,10 @@ function handle_command_options() {
DESIGNATED_DOCKER_VERSION="$2" DESIGNATED_DOCKER_VERSION="$2"
shift shift
else else
output_error "命令选项 ${BLUE}$2${PLAIN} 无效,请在该选项后指定有效的版本号" command_error "$2" "有效的版本号"
fi fi
else else
output_error "命令选项 ${BLUE}$1${PLAIN} 无效,请在该选项后指定版本号!" command_error "$1" "版本号"
fi fi
;; ;;
## WEB 协议HTTP/HTTPS ## WEB 协议HTTP/HTTPS
@@ -262,11 +261,11 @@ function handle_command_options() {
shift shift
;; ;;
*) *)
output_error "检测到 ${BLUE}$2${PLAIN} 为无效参数值,请在该选项后指定 http 或 https " command_error "$2" " http 或 https "
;; ;;
esac esac
else else
output_error "命令选项 ${BLUE}$1${PLAIN} 无效,请在该选项后指定 WEB 协议http/https" ocommand_error "$1" " WEB 协议 (http/https) "
fi fi
;; ;;
## 安装最新版本 ## 安装最新版本
@@ -278,11 +277,11 @@ function handle_command_options() {
shift shift
;; ;;
*) *)
output_error "命令选项 ${BLUE}$2${PLAIN} 无效,请在该选项后指定 true 或 false " command_error "$2" " true 或 false "
;; ;;
esac esac
else else
output_error "命令选项 ${BLUE}$1${PLAIN} 无效,请在该选项后指定 true 或 false " command_error "$1" " true 或 false "
fi fi
;; ;;
## 忽略覆盖备份提示 ## 忽略覆盖备份提示
@@ -298,11 +297,11 @@ function handle_command_options() {
shift shift
;; ;;
*) *)
output_error "命令选项 ${BLUE}$2${PLAIN} 无效,请在该选项后指定 true 或 false " command_error "$2" " true 或 false "
;; ;;
esac esac
else else
output_error "命令选项 ${BLUE}$1${PLAIN} 无效,请在该选项后指定 true 或 false " command_error "$1" " true 或 false "
fi fi
;; ;;
## 清除屏幕上的所有内容 ## 清除屏幕上的所有内容
@@ -314,11 +313,11 @@ function handle_command_options() {
shift shift
;; ;;
*) *)
output_error "命令选项 ${BLUE}$2${PLAIN} 无效,请在该选项后指定 true 或 false " command_error "$2" " true 或 false "
;; ;;
esac esac
else else
output_error "命令选项 ${BLUE}$1${PLAIN} 无效,请在该选项后指定 true 或 false " command_error "$1" " true 或 false "
fi fi
;; ;;
## 仅更换镜像仓库模式 ## 仅更换镜像仓库模式
@@ -335,7 +334,7 @@ function handle_command_options() {
exit exit
;; ;;
*) *)
output_error "命令选项 ${BLUE}$1${PLAIN} 无效,请确认后重新输入!" command_error "$1"
;; ;;
esac esac
shift shift
@@ -364,7 +363,6 @@ function run_start() {
echo -e "欢迎使用 Docker Engine 安装与换源脚本" echo -e "欢迎使用 Docker Engine 安装与换源脚本"
} }
## 运行结束
function run_end() { function run_end() {
if [[ "${PURE_MODE}" == "true" ]]; then if [[ "${PURE_MODE}" == "true" ]]; then
echo '' echo ''
@@ -381,25 +379,41 @@ function run_end() {
echo -e "\n\033[3;1mPowered by \033[34mLinuxMirrors\033[0m\n" echo -e "\n\033[3;1mPowered by \033[34mLinuxMirrors\033[0m\n"
} }
## 报错退出
function output_error() { function output_error() {
[ "$1" ] && echo -e "\n$ERROR $1\n" [ "$1" ] && echo -e "\n$ERROR $1\n"
exit 1 exit 1
} }
## 检查命令是否存在 function command_error() {
local tmp_text="请确认后重新输入"
if [[ "${2}" ]]; then
tmp_text="请在该选项后指定${2}"
fi
output_error "命令选项 ${BLUE}$1${PLAIN} 无效,${tmp_text}"
}
function unsupport_system_error() {
local tmp_text=""
if [[ "${2}" ]]; then
tmp_text=",请参考如下命令自行安装:\n\n${BLUE}$2${PLAIN}"
fi
output_error "不支持当前操作系统($1${tmp_text}"
}
function input_error() {
echo -e "\n$WARN 输入错误,$1"
}
function command_exists() { function command_exists() {
command -v "$@" &>/dev/null command -v "$@" &>/dev/null
} }
## 权限判定
function permission_judgment() { function permission_judgment() {
if [ $UID -ne 0 ]; then if [ $UID -ne 0 ]; then
output_error "权限不足,请使用 Root 用户运行本脚本" output_error "权限不足,请使用 Root 用户运行本脚本"
fi fi
} }
## 收集系统信息
function collect_system_info() { function collect_system_info() {
## 定义系统名称 ## 定义系统名称
SYSTEM_NAME="$(cat $File_LinuxRelease | grep -E "^NAME=" | awk -F '=' '{print$2}' | sed "s/[\'\"]//g")" SYSTEM_NAME="$(cat $File_LinuxRelease | grep -E "^NAME=" | awk -F '=' '{print$2}' | sed "s/[\'\"]//g")"
@@ -413,8 +427,6 @@ function collect_system_info() {
## 判定当前系统派系 ## 判定当前系统派系
if [ -s "${File_DebianVersion}" ]; then if [ -s "${File_DebianVersion}" ]; then
SYSTEM_FACTIONS="${SYSTEM_DEBIAN}" SYSTEM_FACTIONS="${SYSTEM_DEBIAN}"
elif [ -s "${File_OracleLinuxRelease}" ]; then
[[ "${ONLY_REGISTRY}" != "true" ]] && output_error "不支持当前操作系统,请参考如下命令自行安装:\n\ndnf install -y docker\nsystemctl enable --now docker"
elif [ -s "${File_RedHatRelease}" ]; then elif [ -s "${File_RedHatRelease}" ]; then
SYSTEM_FACTIONS="${SYSTEM_REDHAT}" SYSTEM_FACTIONS="${SYSTEM_REDHAT}"
elif [ -s "${File_openEulerRelease}" ]; then elif [ -s "${File_openEulerRelease}" ]; then
@@ -422,27 +434,27 @@ function collect_system_info() {
elif [ -s "${File_OpenCloudOSRelease}" ]; then elif [ -s "${File_OpenCloudOSRelease}" ]; then
# 拦截 OpenCloudOS 9 及以上版本,不支持从 Docker 官方仓库安装 # 拦截 OpenCloudOS 9 及以上版本,不支持从 Docker 官方仓库安装
if [[ "${SYSTEM_VERSION_ID_MAJOR}" -ge 9 ]]; then if [[ "${SYSTEM_VERSION_ID_MAJOR}" -ge 9 ]]; then
[[ "${ONLY_REGISTRY}" != "true" ]] && output_error "不支持当前操作系统,请参考如下命令自行安装:\n\ndnf install -y docker\nsystemctl enable --now docker" [[ "${ONLY_REGISTRY}" != "true" ]] && unsupport_system_error "OpenCloudOS 9" "dnf install -y docker\nsystemctl enable --now docker"
fi fi
SYSTEM_FACTIONS="${SYSTEM_OPENCLOUDOS}" # 自 9.0 版本起不再基于红帽 SYSTEM_FACTIONS="${SYSTEM_OPENCLOUDOS}" # 自 9.0 版本起不再基于红帽
elif [ -s "${File_AnolisOSRelease}" ]; then elif [ -s "${File_AnolisOSRelease}" ]; then
# 拦截 Anolis OS 8.8 及以上版本,不支持从 Docker 官方仓库安装23 版本支持 # 拦截 Anolis OS 8.8 及以上版本,不支持从 Docker 官方仓库安装23 版本支持
if [[ "${SYSTEM_VERSION_ID_MAJOR}" == 8 ]]; then if [[ "${SYSTEM_VERSION_ID_MAJOR}" == 8 ]]; then
[[ "${ONLY_REGISTRY}" != "true" ]] && output_error "不支持当前操作系统,请参考如下命令自行安装:\n\ndnf install -y docker\nsystemctl enable --now docker" [[ "${ONLY_REGISTRY}" != "true" ]] && unsupport_system_error "Anolis OS 8" "dnf install -y docker\nsystemctl enable --now docker"
fi fi
SYSTEM_FACTIONS="${SYSTEM_ANOLISOS}" # 自 8.8 版本起不再基于红帽 SYSTEM_FACTIONS="${SYSTEM_ANOLISOS}" # 自 8.8 版本起不再基于红帽
elif [ -s "${File_openKylinVersion}" ]; then elif [ -s "${File_openKylinVersion}" ]; then
[[ "${ONLY_REGISTRY}" != "true" ]] && output_error "不支持当前操作系统,请参考如下命令自行安装:\n\napt-get install -y docker\nsystemctl enable --now docker" [[ "${ONLY_REGISTRY}" != "true" ]] && unsupport_system_error "openKylin" "apt-get install -y docker\nsystemctl enable --now docker"
elif [ -f "${File_ArchLinuxRelease}" ]; then elif [ -f "${File_ArchLinuxRelease}" ]; then
[[ "${ONLY_REGISTRY}" != "true" ]] && output_error "不支持当前操作系统,请参考如下命令自行安装:\n\npacman -S docker\nsystemctl enable --now docker" [[ "${ONLY_REGISTRY}" != "true" ]] && unsupport_system_error "Arch Linux" "pacman -S docker\nsystemctl enable --now docker"
elif [ -f "${File_GentooRelease}" ]; then elif [ -f "${File_GentooRelease}" ]; then
[[ "${ONLY_REGISTRY}" != "true" ]] && output_error "不支持当前操作系统(Gentoo" [[ "${ONLY_REGISTRY}" != "true" ]] && unsupport_system_error "Gentoo"
elif [[ "${SYSTEM_NAME}" == *"openSUSE"* ]]; then elif [[ "${SYSTEM_NAME}" == *"openSUSE"* ]]; then
[[ "${ONLY_REGISTRY}" != "true" ]] && output_error "不支持当前操作系统,请参考如下命令自行安装:\n\nzypper install docker\nsystemctl enable --now docker" [[ "${ONLY_REGISTRY}" != "true" ]] && unsupport_system_error "openSUSE" "zypper install docker\nsystemctl enable --now docker"
elif [[ "${SYSTEM_NAME}" == *"NixOS"* ]]; then elif [[ "${SYSTEM_NAME}" == *"NixOS"* ]]; then
[[ "${ONLY_REGISTRY}" != "true" ]] && output_error "不支持当前操作系统(NixOS" [[ "${ONLY_REGISTRY}" != "true" ]] && unsupport_system_error "NixOS"
else else
output_error "不支持当前操作系统" unsupport_system_error "未知的系统"
fi fi
## 判定系统类型、版本、版本号 ## 判定系统类型、版本、版本号
case "${SYSTEM_FACTIONS}" in case "${SYSTEM_FACTIONS}" in
@@ -466,13 +478,15 @@ function collect_system_info() {
SYSTEM_JUDGMENT="$(awk '{printf $1}' $File_RedHatRelease)" SYSTEM_JUDGMENT="$(awk '{printf $1}' $File_RedHatRelease)"
# 拦截 Anolis OS 8.8 以下版本,不支持从 Docker 官方仓库安装 # 拦截 Anolis OS 8.8 以下版本,不支持从 Docker 官方仓库安装
if [[ "${SYSTEM_JUDGMENT}" == "${SYSTEM_ANOLISOS}" ]]; then if [[ "${SYSTEM_JUDGMENT}" == "${SYSTEM_ANOLISOS}" ]]; then
[[ "${ONLY_REGISTRY}" != "true" ]] && output_error "不支持当前操作系统,请参考如下命令自行安装:\n\ndnf install -y docker\nsystemctl enable --now docker" [[ "${ONLY_REGISTRY}" != "true" ]] && unsupport_system_error "Anolis OS 8" "dnf install -y docker\nsystemctl enable --now docker"
fi fi
## 特殊系统判断 ## 特殊系统判断
# Red Hat Enterprise Linux # Red Hat Enterprise Linux
grep -q "${SYSTEM_RHEL}" $File_RedHatRelease && SYSTEM_JUDGMENT="${SYSTEM_RHEL}" grep -q "${SYSTEM_RHEL}" $File_RedHatRelease && SYSTEM_JUDGMENT="${SYSTEM_RHEL}"
# CentOS Stream # CentOS Stream
grep -q "${SYSTEM_CENTOS_STREAM}" $File_RedHatRelease && SYSTEM_JUDGMENT="${SYSTEM_CENTOS_STREAM}" grep -q "${SYSTEM_CENTOS_STREAM}" $File_RedHatRelease && SYSTEM_JUDGMENT="${SYSTEM_CENTOS_STREAM}"
# Oracle Linux
[ -s "${File_OracleLinuxRelease}" ] && SYSTEM_JUDGMENT="${SYSTEM_ORACLE}"
;; ;;
*) *)
SYSTEM_JUDGMENT="${SYSTEM_FACTIONS}" SYSTEM_JUDGMENT="${SYSTEM_FACTIONS}"
@@ -547,9 +561,11 @@ function collect_system_info() {
;; ;;
"${SYSTEM_RHEL}") "${SYSTEM_RHEL}")
SOURCE_BRANCH="rhel" SOURCE_BRANCH="rhel"
# 拦截 RHEL 最新的 10 版本 # RHEL 10
if [[ "${SYSTEM_VERSION_ID_MAJOR}" == 10 ]]; then if [[ "${SYSTEM_VERSION_ID_MAJOR}" == 10 ]]; then
output_error "暂不支持当前操作系统RHEL 10请等待 Docker 官方适配或使用正版操作系统软件源进行安装" echo -e "\n$WARN 当前采用 centos 分支(红帽衍生操作系统安装方式)进行安装,可能存在某些无法预料的兼容性问题!"
echo -e "\n$TIP Docker 官方尚未支持 RHEL 10 且红帽官方已将 Docker 从注册软件源中移除并默认使用 Podman。"
SOURCE_BRANCH="centos"
fi fi
;; ;;
*) *)
@@ -706,16 +722,17 @@ function choose_protocol() {
if [[ "${ONLY_HTTP}" == "true" ]]; then if [[ "${ONLY_HTTP}" == "true" ]]; then
WEB_PROTOCOL="http" WEB_PROTOCOL="http"
else else
local ask_text="软件源是否使用 HTTP 协议?"
if [[ "${CAN_USE_ADVANCED_INTERACTIVE_SELECTION}" == "true" ]]; then if [[ "${CAN_USE_ADVANCED_INTERACTIVE_SELECTION}" == "true" ]]; then
echo '' echo ''
interactive_select_boolean "${BOLD}软件源是否使用 HTTP 协议?${PLAIN}" interactive_select_boolean "${BOLD}${ask_text}${PLAIN}"
if [[ "${_SELECT_RESULT}" == "true" ]]; then if [[ "${_SELECT_RESULT}" == "true" ]]; then
WEB_PROTOCOL="http" WEB_PROTOCOL="http"
else else
WEB_PROTOCOL="https" WEB_PROTOCOL="https"
fi fi
else else
local CHOICE=$(echo -e "\n${BOLD}└─ 软件源是否使用 HTTP 协议? [Y/n] ${PLAIN}") local CHOICE="$(echo -e "\n${BOLD}└─ ${ask_text} [Y/n] ${PLAIN}")"
read -rp "${CHOICE}" INPUT read -rp "${CHOICE}" INPUT
[[ -z "${INPUT}" ]] && INPUT=Y [[ -z "${INPUT}" ]] && INPUT=Y
case "${INPUT}" in case "${INPUT}" in
@@ -726,7 +743,7 @@ function choose_protocol() {
WEB_PROTOCOL="https" WEB_PROTOCOL="https"
;; ;;
*) *)
echo -e "\n$WARN 输入错误,默认使用 HTTPS 协议" input_error "默认使用 HTTPS 协议"
WEB_PROTOCOL="https" WEB_PROTOCOL="https"
;; ;;
esac esac
@@ -743,14 +760,15 @@ function close_firewall_service() {
fi fi
if [[ "$(systemctl is-active firewalld)" == "active" ]]; then if [[ "$(systemctl is-active firewalld)" == "active" ]]; then
if [[ -z "${CLOSE_FIREWALL}" ]]; then if [[ -z "${CLOSE_FIREWALL}" ]]; then
local ask_text="是否关闭系统防火墙和 SELinux ?"
if [[ "${CAN_USE_ADVANCED_INTERACTIVE_SELECTION}" == "true" ]]; then if [[ "${CAN_USE_ADVANCED_INTERACTIVE_SELECTION}" == "true" ]]; then
echo '' echo ''
interactive_select_boolean "${BOLD}是否关闭系统防火墙和 SELinux ?${PLAIN}" interactive_select_boolean "${BOLD}${ask_text}${PLAIN}"
if [[ "${_SELECT_RESULT}" == "true" ]]; then if [[ "${_SELECT_RESULT}" == "true" ]]; then
CLOSE_FIREWALL="true" CLOSE_FIREWALL="true"
fi fi
else else
local CHOICE=$(echo -e "\n${BOLD}└─ 是否关闭系统防火墙和 SELinux ? [Y/n] ${PLAIN}") local CHOICE="$(echo -e "\n${BOLD}└─ ${ask_text} [Y/n] ${PLAIN}")"
read -rp "${CHOICE}" INPUT read -rp "${CHOICE}" INPUT
[[ -z "${INPUT}" ]] && INPUT=Y [[ -z "${INPUT}" ]] && INPUT=Y
case "${INPUT}" in case "${INPUT}" in
@@ -759,7 +777,7 @@ function close_firewall_service() {
;; ;;
[Nn] | [Nn][Oo]) ;; [Nn] | [Nn][Oo]) ;;
*) *)
echo -e "\n$WARN 输入错误,默认不关闭" input_error "默认不关闭"
;; ;;
esac esac
fi fi
@@ -1004,7 +1022,7 @@ function install_docker_engine() {
target_docker_version="${_SELECT_RESULT}" target_docker_version="${_SELECT_RESULT}"
echo -e "\n${GREEN}${PLAIN} ${BOLD}指定安装版本:${target_docker_version}${PLAIN}\n" echo -e "\n${GREEN}${PLAIN} ${BOLD}指定安装版本:${target_docker_version}${PLAIN}\n"
else else
echo -e "\n${GREEN} --------- 请选择你要安装的版本27.4.0 ---------- ${PLAIN}\n" echo -e "\n${GREEN} --------- 请选择你要安装的版本28.3.0 ---------- ${PLAIN}\n"
cat $File_DockerVersionTmp cat $File_DockerVersionTmp
while true; do while true; do
local CHOICE=$(echo -e "\n${BOLD}└─ 请根据上面的列表,选择并输入你想要安装的具体版本号:${PLAIN}\n") local CHOICE=$(echo -e "\n${BOLD}└─ 请根据上面的列表,选择并输入你想要安装的具体版本号:${PLAIN}\n")
@@ -1080,16 +1098,17 @@ function install_docker_engine() {
## 判断是否手动选择安装版本 ## 判断是否手动选择安装版本
if [[ -z "${INSTALL_LATESTED_DOCKER}" ]]; then if [[ -z "${INSTALL_LATESTED_DOCKER}" ]]; then
local ask_text="是否安装最新版本的 Docker Engine ?"
if [[ "${CAN_USE_ADVANCED_INTERACTIVE_SELECTION}" == "true" ]]; then if [[ "${CAN_USE_ADVANCED_INTERACTIVE_SELECTION}" == "true" ]]; then
echo '' echo ''
interactive_select_boolean "${BOLD}是否安装最新版本的 Docker Engine ?${PLAIN}" interactive_select_boolean "${BOLD}${ask_text}${PLAIN}"
if [[ "${_SELECT_RESULT}" == "true" ]]; then if [[ "${_SELECT_RESULT}" == "true" ]]; then
INSTALL_LATESTED_DOCKER="true" INSTALL_LATESTED_DOCKER="true"
else else
INSTALL_LATESTED_DOCKER="false" INSTALL_LATESTED_DOCKER="false"
fi fi
else else
local CHOICE_A=$(echo -e "\n${BOLD}└─ 是否安装最新版本的 Docker Engine ? [Y/n] ${PLAIN}") local CHOICE_A="$(echo -e "\n${BOLD}└─ ${ask_text} [Y/n] ${PLAIN}")"
read -p "${CHOICE_A}" INPUT read -p "${CHOICE_A}" INPUT
[[ -z "${INPUT}" ]] && INPUT=Y [[ -z "${INPUT}" ]] && INPUT=Y
case $INPUT in case $INPUT in
@@ -1101,10 +1120,11 @@ function install_docker_engine() {
;; ;;
*) *)
INSTALL_LATESTED_DOCKER="true" INSTALL_LATESTED_DOCKER="true"
echo -e "\n$WARN 输入错误,默认安装最新版本" input_error "默认安装最新版本"
;; ;;
esac esac
fi fi
echo ''
fi fi
## 判定是否已安装 ## 判定是否已安装
@@ -1158,15 +1178,16 @@ function change_docker_registry_mirror() {
if [ -d "${Dir_Docker}" ] && [ -e "${File_DockerConfig}" ]; then if [ -d "${Dir_Docker}" ] && [ -e "${File_DockerConfig}" ]; then
if [ -e "${File_DockerConfigBackup}" ]; then if [ -e "${File_DockerConfigBackup}" ]; then
if [[ "${IGNORE_BACKUP_TIPS}" == "false" ]]; then if [[ "${IGNORE_BACKUP_TIPS}" == "false" ]]; then
local ask_text="检测到已备份的 Docker 配置文件,是否跳过覆盖备份?"
if [[ "${CAN_USE_ADVANCED_INTERACTIVE_SELECTION}" == "true" ]]; then if [[ "${CAN_USE_ADVANCED_INTERACTIVE_SELECTION}" == "true" ]]; then
echo '' echo ''
interactive_select_boolean "${BOLD}检测到已备份的 Docker 配置文件,是否跳过覆盖备份?${PLAIN}" interactive_select_boolean "${BOLD}${ask_text}${PLAIN}"
if [[ "${_SELECT_RESULT}" == "false" ]]; then if [[ "${_SELECT_RESULT}" == "false" ]]; then
echo '' echo ''
cp -rvf $File_DockerConfig $File_DockerConfigBackup 2>&1 cp -rvf $File_DockerConfig $File_DockerConfigBackup 2>&1
fi fi
else else
local CHOICE_BACKUP=$(echo -e "\n${BOLD}└─ 检测到已备份的 Docker 配置文件,是否跳过覆盖备份? [Y/n] ${PLAIN}") local CHOICE_BACKUP="$(echo -e "\n${BOLD}└─ ${ask_text} [Y/n] ${PLAIN}")"
read -p "${CHOICE_BACKUP}" INPUT read -p "${CHOICE_BACKUP}" INPUT
[[ -z "${INPUT}" ]] && INPUT=Y [[ -z "${INPUT}" ]] && INPUT=Y
case $INPUT in case $INPUT in
@@ -1176,7 +1197,7 @@ function change_docker_registry_mirror() {
cp -rvf $File_DockerConfig $File_DockerConfigBackup 2>&1 cp -rvf $File_DockerConfig $File_DockerConfigBackup 2>&1
;; ;;
*) *)
echo -e "\n$WARN 输入错误,默认不覆盖" input_error "默认不覆盖"
;; ;;
esac esac
fi fi
@@ -1289,18 +1310,20 @@ function check_installed_result() {
echo -e " $(docker compose version 2>&1)" echo -e " $(docker compose version 2>&1)"
# echo -e "\n$COMPLETE 安装完成" # echo -e "\n$COMPLETE 安装完成"
else else
echo -e "\n$ERROR 安装失败" echo -e "\n$FAIL 安装失败"
local source_file package_manager
case "${SYSTEM_FACTIONS}" in case "${SYSTEM_FACTIONS}" in
"${SYSTEM_DEBIAN}") "${SYSTEM_DEBIAN}")
echo -e "\n检查源文件cat $Dir_AptAdditionalSources/docker.list" source_file="${Dir_AptAdditionalSources}/docker.list"
echo -e '请尝试手动执行安装命令apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin\n' package_manager="apt-get"
;; ;;
"${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}") "${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}")
local package_manager="$(get_package_manager)" source_file="${Dir_YumRepos}/docker.repo"
echo -e "\n检查源文件cat ${Dir_YumRepos}/docker.repo" package_manager="$(get_package_manager)"
echo -e "请尝试手动执行安装命令:${package_manager} install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin\n"
;; ;;
esac esac
echo -e "\n检查源文件cat ${source_file}"
echo -e "请尝试手动执行安装命令:${package_manager} install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin\n"
exit 1 exit 1
fi fi
if [[ $(systemctl is-active docker) != "active" ]]; then if [[ $(systemctl is-active docker) != "active" ]]; then
@@ -1315,7 +1338,7 @@ function check_installed_result() {
fi fi
fi fi
else else
echo -e "\n$ERROR 安装失败" echo -e "\n$FAIL 安装失败"
fi fi
} }
@@ -1344,7 +1367,7 @@ function interactive_select_mirror() {
unset options[${#options[@]}-1] unset options[${#options[@]}-1]
local selected=0 local selected=0
local start=0 local start=0
local page_size=$(($(tput lines 2>/dev/null) - 3)) # 减去3行用于显示提示信息 local page_size=$(($(tput lines 2>/dev/null) - 3))
function clear_menu() { function clear_menu() {
tput rc 2>/dev/null tput rc 2>/dev/null
for ((i = 0; i < ${#options[@]} + 1; i++)); do for ((i = 0; i < ${#options[@]} + 1; i++)); do
@@ -1384,17 +1407,15 @@ function interactive_select_mirror() {
fi fi
echo "$key" echo "$key"
} }
tput smcup 2>/dev/null # 保存当前屏幕并切换到新屏幕 tput smcup 2>/dev/null
tput sc 2>/dev/null # 保存光标位置 tput sc 2>/dev/null
tput civis 2>/dev/null # 隐藏光标 tput civis 2>/dev/null
trap "cleanup" INT TERM # 捕捉脚本结束时恢复光标 trap "cleanup" INT TERM
draw_menu # 初始化菜单位置 draw_menu
# 处理选择
while true; do while true; do
key=$(read_key) key=$(read_key)
case "$key" in case "$key" in
"[A" | "w" | "W") "[A" | "w" | "W")
# 上箭头 / W
if [ "$selected" -gt 0 ]; then if [ "$selected" -gt 0 ]; then
selected=$((selected - 1)) selected=$((selected - 1))
if [ "$selected" -lt "$start" ]; then if [ "$selected" -lt "$start" ]; then
@@ -1403,7 +1424,6 @@ function interactive_select_mirror() {
fi fi
;; ;;
"[B" | "s" | "S") "[B" | "s" | "S")
# 下箭头 / S
if [ "$selected" -lt $((${#options[@]} - 1)) ]; then if [ "$selected" -lt $((${#options[@]} - 1)) ]; then
selected=$((selected + 1)) selected=$((selected + 1))
if [ "$selected" -ge $((start + page_size)) ]; then if [ "$selected" -ge $((start + page_size)) ]; then
@@ -1412,7 +1432,6 @@ function interactive_select_mirror() {
fi fi
;; ;;
"") "")
# Enter 键
tput rmcup tput rmcup
break break
;; ;;
@@ -1420,11 +1439,8 @@ function interactive_select_mirror() {
esac esac
draw_menu draw_menu
done done
# clear_menu # 清除菜单 tput cnorm 2>/dev/null
tput cnorm 2>/dev/null # 恢复光标 tput rmcup 2>/dev/null
tput rmcup 2>/dev/null # 恢复之前的屏幕
# tput rc 2>/dev/null # 恢复光标位置
# 处理结果
_SELECT_RESULT="${options[$selected]}" _SELECT_RESULT="${options[$selected]}"
} }
@@ -1432,17 +1448,15 @@ function interactive_select_boolean() {
_SELECT_RESULT="" _SELECT_RESULT=""
local selected=0 local selected=0
local message="$1" local message="$1"
local menu_height=3 # 菜单总高度(标题行+空行+选项行) local menu_height=3
local original_line local original_line
function store_position() { function store_position() {
# 保存菜单开始前的行位置
original_line=$(tput lines 2>/dev/null) original_line=$(tput lines 2>/dev/null)
} }
function clear_menu() { function clear_menu() {
# 向上移动到菜单开始位置并清除菜单
for ((i = 0; i < ${menu_height}; i++)); do for ((i = 0; i < ${menu_height}; i++)); do
tput cuu1 2>/dev/null # 光标上移一行 tput cuu1 2>/dev/null
tput el 2>/dev/null # 清除当前行 tput el 2>/dev/null
done done
} }
function cleanup() { function cleanup() {
@@ -1452,7 +1466,6 @@ function interactive_select_boolean() {
exit 130 exit 130
} }
function draw_menu() { function draw_menu() {
# 绘制菜单不改变光标位置
echo -e "╭─ ${message}" echo -e "╭─ ${message}"
echo -e "" echo -e ""
if [ "$selected" -eq 0 ]; then if [ "$selected" -eq 0 ]; then
@@ -1469,16 +1482,14 @@ function interactive_select_boolean() {
fi fi
echo "$key" echo "$key"
} }
tput civis 2>/dev/null # 隐藏光标 tput civis 2>/dev/null
store_position # 记录当前位置 store_position
trap "cleanup" INT TERM trap "cleanup" INT TERM
draw_menu # 初始化菜单位置 draw_menu
# 处理选择
while true; do while true; do
key=$(read_key) key=$(read_key)
case "$key" in case "$key" in
"[D" | "a" | "A") "[D" | "a" | "A")
# 左箭头 / A
if [ "$selected" -gt 0 ]; then if [ "$selected" -gt 0 ]; then
selected=$((selected - 1)) selected=$((selected - 1))
clear_menu clear_menu
@@ -1486,7 +1497,6 @@ function interactive_select_boolean() {
fi fi
;; ;;
"[C" | "d" | "D") "[C" | "d" | "D")
# 右箭头 / D
if [ "$selected" -lt 1 ]; then if [ "$selected" -lt 1 ]; then
selected=$((selected + 1)) selected=$((selected + 1))
clear_menu clear_menu
@@ -1494,8 +1504,7 @@ function interactive_select_boolean() {
fi fi
;; ;;
"") "")
# Enter 键 clear_menu
clear_menu # 先清除菜单
break break
;; ;;
*) ;; *) ;;
@@ -1510,68 +1519,53 @@ function interactive_select_boolean() {
echo -e "╰─ \033[2m○ 是 / \033[0m\033[32m●\033[0m \033[1m否\033[0m" echo -e "╰─ \033[2m○ 是 / \033[0m\033[32m●\033[0m \033[1m否\033[0m"
_SELECT_RESULT="false" _SELECT_RESULT="false"
fi fi
tput cnorm 2>/dev/null # 恢复光标 tput cnorm 2>/dev/null
} }
function animate_exec() { function animate_exec() {
local cmd="$1" local cmd="$1"
local title="$2" local title="$2"
local max_lines=${3:-5} # 默认显示5行 local max_lines=${3:-5}
local spinner_style="${4:-dots}" # 动画样式 local spinner_style="${4:-dots}"
local refresh_rate="${5:-0.1}" # 刷新率 local refresh_rate="${5:-0.1}"
# 动画样式
local -A spinners=([dots]="⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏" [circle]="◐ ◓ ◑ ◒" [classic]="-\\ |/") local -A spinners=([dots]="⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏" [circle]="◐ ◓ ◑ ◒" [classic]="-\\ |/")
local -A recommended_rates=([dots]="0.08" [circle]="0.12" [classic]="0.12") local -A recommended_rates=([dots]="0.08" [circle]="0.12" [classic]="0.12")
[[ -z "${spinners[$spinner_style]}" ]] && spinner_style="dots" [[ -z "${spinners[$spinner_style]}" ]] && spinner_style="dots"
[[ "${refresh_rate}" == "0.1" ]] && refresh_rate="${recommended_rates[$spinner_style]}" [[ "${refresh_rate}" == "0.1" ]] && refresh_rate="${recommended_rates[$spinner_style]}"
# 获取终端宽度
local term_width=$(tput cols 2>/dev/null || echo 80) local term_width=$(tput cols 2>/dev/null || echo 80)
local display_width=$((term_width - 2)) local display_width=$((term_width - 2))
# 截断行
function simple_truncate() { function simple_truncate() {
local line="$1" local line="$1"
local truncate_marker="..." local truncate_marker="..."
local max_length=$((display_width - 3)) local max_length=$((display_width - 3))
# 快速判断如果ASCII行长度在范围内直接返回
if [[ "${line}" =~ ^[[:ascii:]]*$ && ${#line} -le $display_width ]]; then if [[ "${line}" =~ ^[[:ascii:]]*$ && ${#line} -le $display_width ]]; then
echo "${line}" echo "${line}"
return return
fi fi
# 1. 计算非ASCII字符数量
local non_ascii_count=$(echo "${line// /}" | sed "s|[0-9a-zA-Z\.\=\:\_\(\)\'\"-\/\!·]||g;" | wc -m) local non_ascii_count=$(echo "${line// /}" | sed "s|[0-9a-zA-Z\.\=\:\_\(\)\'\"-\/\!·]||g;" | wc -m)
# 2. 总字符数
local total_length=${#line} local total_length=${#line}
# 3. 实际显示宽度 = 总字符数 + 非ASCII字符数
# 非ASCII字符额外占用1个宽度单位即总共2个
local display_length=$((total_length + non_ascii_count)) local display_length=$((total_length + non_ascii_count))
# 4. 中文引号特殊处理引号只占1个宽度需要减去额外计算的部分
local quote_count=0 local quote_count=0
[[ $(echo "${line}" | grep -c "“") -gt 0 ]] && quote_count=$((quote_count + $(echo "${line}" | grep -c "“"))) [[ $(echo "${line}" | grep -c "“") -gt 0 ]] && quote_count=$((quote_count + $(echo "${line}" | grep -c "“")))
[[ $(echo "${line}" | grep -c "”") -gt 0 ]] && quote_count=$((quote_count + $(echo "${line}" | grep -c "”"))) [[ $(echo "${line}" | grep -c "”") -gt 0 ]] && quote_count=$((quote_count + $(echo "${line}" | grep -c "”")))
[[ $(echo "${line}" | grep -c "") -gt 0 ]] && quote_count=$((quote_count + $(echo "${line}" | grep -c ""))) [[ $(echo "${line}" | grep -c "") -gt 0 ]] && quote_count=$((quote_count + $(echo "${line}" | grep -c "")))
[[ $(echo "${line}" | grep -c "") -gt 0 ]] && quote_count=$((quote_count + $(echo "${line}" | grep -c ""))) [[ $(echo "${line}" | grep -c "") -gt 0 ]] && quote_count=$((quote_count + $(echo "${line}" | grep -c "")))
# 5. 调整宽度(减去引号额外计算的部分)
display_length=$((display_length - quote_count)) display_length=$((display_length - quote_count))
# 如果计算宽度在显示范围内,直接显示
if [[ $display_length -le $display_width ]]; then if [[ $display_length -le $display_width ]]; then
echo "$line" echo "$line"
return return
fi fi
# 需要截断,逐字符构建
local result="" local result=""
local current_width=0 local current_width=0
local i=0 local i=0
while [ $i -lt ${#line} ]; do while [ $i -lt ${#line} ]; do
local char="${line:$i:1}" local char="${line:$i:1}"
local char_width=1 local char_width=1
# 是否是中文等宽字符(非ASCII)
if ! [[ "$char" =~ [0-9a-zA-Z\.\=\:\_\(\)\'\"-\/\!·] ]]; then if ! [[ "$char" =~ [0-9a-zA-Z\.\=\:\_\(\)\'\"-\/\!·] ]]; then
# 不是中文引号则算2个宽度
if [[ "$char" != "" && "$char" != "" && "$char" != "" && "$char" != "" ]]; then if [[ "$char" != "" && "$char" != "" && "$char" != "" && "$char" != "" ]]; then
char_width=2 char_width=2
fi fi
fi fi
# 检查添加此字符是否会超出限制
if [[ $((current_width + char_width)) -gt $max_length ]]; then if [[ $((current_width + char_width)) -gt $max_length ]]; then
echo "${result}${truncate_marker}" echo "${result}${truncate_marker}"
return return
@@ -1580,17 +1574,14 @@ function animate_exec() {
current_width=$((current_width + char_width)) current_width=$((current_width + char_width))
((i++)) ((i++))
done done
# 完整遍历未超出限制
echo "${line}" echo "${line}"
} }
# 清理函数
function cleanup() { function cleanup() {
[ -f "${temp_file}" ] && rm -f "${temp_file}" [ -f "${temp_file}" ] && rm -f "${temp_file}"
tput cnorm 2>/dev/null tput cnorm 2>/dev/null
echo -e "\n\033[1;44m 提示 \033[0m \033[31m操作已取消\033[0m\n" echo -e "\n\033[1;44m 提示 \033[0m \033[31m操作已取消\033[0m\n"
exit 130 exit 130
} }
# 创建临时文件
function make_temp_file() { function make_temp_file() {
local temp_dirs=("." "/tmp") local temp_dirs=("." "/tmp")
local tmp_file="" local tmp_file=""
@@ -1605,65 +1596,49 @@ function animate_exec() {
done done
echo "${tmp_file}" echo "${tmp_file}"
} }
# 更新显示
function update_display() { function update_display() {
local current_size=$(wc -c <"${temp_file}" 2>/dev/null || echo 0) local current_size=$(wc -c <"${temp_file}" 2>/dev/null || echo 0)
# 如果文件大小没变,不更新
if [[ $current_size -le $last_size ]]; then if [[ $current_size -le $last_size ]]; then
return 1 return 1
fi fi
# 只在必要时读取文件
local -a lines=() local -a lines=()
mapfile -t -n "${max_lines}" lines < <(tail -n "$max_lines" "${temp_file}") mapfile -t -n "${max_lines}" lines < <(tail -n "$max_lines" "${temp_file}")
# 处理每一行
local -a processed_lines=() local -a processed_lines=()
for ((i = 0; i < ${#lines[@]}; i++)); do for ((i = 0; i < ${#lines[@]}; i++)); do
processed_lines[i]=$(simple_truncate "${lines[i]}") processed_lines[i]=$(simple_truncate "${lines[i]}")
done done
# 更新显示 tput cud1 2>/dev/null
tput cud1 2>/dev/null # 移动到标题下 echo -ne "\r\033[K"
echo -ne "\r\033[K" # 清空当前行 tput cud1 2>/dev/null
tput cud1 2>/dev/null # 移动到内容区
# 显示处理后的行
for ((i = 0; i < $max_lines; i++)); do for ((i = 0; i < $max_lines; i++)); do
echo -ne "\r\033[K" # 清空当前行 echo -ne "\r\033[K"
[[ $i -lt ${#processed_lines[@]} ]] && echo -ne "\033[2m${processed_lines[$i]}\033[0m" [[ $i -lt ${#processed_lines[@]} ]] && echo -ne "\033[2m${processed_lines[$i]}\033[0m"
[[ $i -lt $((max_lines - 1)) ]] && tput cud1 2>/dev/null [[ $i -lt $((max_lines - 1)) ]] && tput cud1 2>/dev/null
done done
# 返回到标题行
for ((i = 0; i < $max_lines + 1; i++)); do for ((i = 0; i < $max_lines + 1; i++)); do
tput cuu1 2>/dev/null tput cuu1 2>/dev/null
done done
# 更新文件大小记录
last_size=$current_size last_size=$current_size
return 0 return 0
} }
# 初始化
local spinner_frames=(${spinners[$spinner_style]}) local spinner_frames=(${spinners[$spinner_style]})
local temp_file="$(make_temp_file)" local temp_file="$(make_temp_file)"
trap "cleanup" INT TERM trap "cleanup" INT TERM
tput civis 2>/dev/null # 隐藏光标 tput civis 2>/dev/null
# 预留显示空间 echo ''
echo '' # 标题行 echo ''
echo '' # 空行
for ((i = 0; i < $max_lines; i++)); do for ((i = 0; i < $max_lines; i++)); do
echo '' echo ''
done done
# 执行命令
eval "${cmd}" >"${temp_file}" 2>&1 & eval "${cmd}" >"${temp_file}" 2>&1 &
local cmd_pid=$! local cmd_pid=$!
local last_size=0 local last_size=0
local spin_idx=0 local spin_idx=0
# 返回到标题行
tput cuu $((max_lines + 2)) 2>/dev/null tput cuu $((max_lines + 2)) 2>/dev/null
# 添加延迟允许命令开始执行
sleep 0.05 sleep 0.05
# 显示初始状态
echo -ne "\r\033[K◉ ${title} [\033[1m\033[34m${spinner_frames[$spin_idx]}\033[0m]" echo -ne "\r\033[K◉ ${title} [\033[1m\033[34m${spinner_frames[$spin_idx]}\033[0m]"
spin_idx=$(((spin_idx + 1) % ${#spinner_frames[@]})) spin_idx=$(((spin_idx + 1) % ${#spinner_frames[@]}))
# 检查初始输出
update_display update_display
# 监控命令执行 - 增加自适应刷新
local update_count=0 local update_count=0
local adaptive_rate=$refresh_rate local adaptive_rate=$refresh_rate
while kill -0 $cmd_pid 2>/dev/null; do while kill -0 $cmd_pid 2>/dev/null; do
@@ -1671,7 +1646,6 @@ function animate_exec() {
spin_idx=$(((spin_idx + 1) % ${#spinner_frames[@]})) spin_idx=$(((spin_idx + 1) % ${#spinner_frames[@]}))
if update_display; then if update_display; then
update_count=$((update_count + 1)) update_count=$((update_count + 1))
# 连续更新太频繁则调整刷新率
if [[ $update_count -gt 5 ]]; then if [[ $update_count -gt 5 ]]; then
adaptive_rate=$(awk "BEGIN {print $adaptive_rate * 1.5; exit}") adaptive_rate=$(awk "BEGIN {print $adaptive_rate * 1.5; exit}")
[[ $(awk "BEGIN {print ($adaptive_rate > 0.5); exit}") -eq 1 ]] && adaptive_rate=0.5 [[ $(awk "BEGIN {print ($adaptive_rate > 0.5); exit}") -eq 1 ]] && adaptive_rate=0.5
@@ -1685,17 +1659,13 @@ function animate_exec() {
done done
wait $cmd_pid wait $cmd_pid
local exit_status=$? local exit_status=$?
# 最后一次更新显示
update_display update_display
# 显示完成状态
if [ $exit_status -eq 0 ]; then if [ $exit_status -eq 0 ]; then
echo -ne "\r\033[K◉ ${title} [\033[1m\033[32m✓\033[0m]\n" echo -ne "\r\033[K◉ ${title} [\033[1m\033[32m✓\033[0m]\n"
else else
echo -ne "\r\033[K◉ ${title} [\033[1m\033[31m✗\033[0m]\n" echo -ne "\r\033[K◉ ${title} [\033[1m\033[31m✗\033[0m]\n"
fi fi
# 空行
echo -ne "\r\033[K\n" echo -ne "\r\033[K\n"
# 显示最终输出
local actual_lines=$(wc -l <"${temp_file}" 2>/dev/null || echo 0) local actual_lines=$(wc -l <"${temp_file}" 2>/dev/null || echo 0)
[[ $actual_lines -gt $max_lines ]] && actual_lines=$max_lines [[ $actual_lines -gt $max_lines ]] && actual_lines=$max_lines
if [[ $actual_lines -gt 0 ]]; then if [[ $actual_lines -gt 0 ]]; then
@@ -1707,7 +1677,6 @@ function animate_exec() {
echo -ne "\r\033[K\033[2m${line}\033[0m\n" echo -ne "\r\033[K\033[2m${line}\033[0m\n"
done done
fi fi
# 清理并返回
tput cnorm 2>/dev/null tput cnorm 2>/dev/null
rm -f "${temp_file}" rm -f "${temp_file}"
return $exit_status return $exit_status