Files
LinuxMirrors/DockerInstallation.sh
Super Manito e26bd0d3f2 优化
2025-10-20 08:17:07 +08:00

2491 lines
107 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
## Author: SuperManito
## Modified: 2025-10-20
## License: MIT
## GitHub: https://github.com/SuperManito/LinuxMirrors
## Website: https://linuxmirrors.cn
## Docker CE 软件源列表
mirror_list_docker_ce=(
"mirrors.aliyun.com/docker-ce"
"mirrors.tencent.com/docker-ce"
"mirrors.huaweicloud.com/docker-ce"
"mirrors.cmecloud.cn/docker-ce"
"mirrors.163.com/docker-ce"
"mirrors.volces.com/docker"
"mirror.azure.cn/docker-ce"
"mirrors.tuna.tsinghua.edu.cn/docker-ce"
"mirrors.pku.edu.cn/docker-ce"
"mirrors.zju.edu.cn/docker-ce"
"mirrors.nju.edu.cn/docker-ce"
"mirror.sjtu.edu.cn/docker-ce"
"mirrors.cqupt.edu.cn/docker-ce"
"mirrors.ustc.edu.cn/docker-ce"
"mirror.iscas.ac.cn/docker-ce"
"download.docker.com"
)
## Docker Registry 仓库列表
mirror_list_registry=(
"docker.1ms.run"
"dockerproxy.net"
"docker.m.daocloud.io"
"docker.1panel.live"
"registry.cn-hangzhou.aliyuncs.com"
"registry.cn-shanghai.aliyuncs.com"
"registry.cn-qingdao.aliyuncs.com"
"registry.cn-beijing.aliyuncs.com"
"registry.cn-zhangjiakou.aliyuncs.com"
"registry.cn-huhehaote.aliyuncs.com"
"registry.cn-wulanchabu.aliyuncs.com"
"registry.cn-shenzhen.aliyuncs.com"
"registry.cn-heyuan.aliyuncs.com"
"registry.cn-guangzhou.aliyuncs.com"
"registry.cn-chengdu.aliyuncs.com"
"registry.cn-hongkong.aliyuncs.com"
"registry.ap-northeast-1.aliyuncs.com"
"registry.ap-southeast-1.aliyuncs.com"
"registry.ap-southeast-3.aliyuncs.com"
"registry.ap-southeast-5.aliyuncs.com"
"registry.eu-central-1.aliyuncs.com"
"registry.eu-west-1.aliyuncs.com"
"registry.us-west-1.aliyuncs.com"
"registry.us-east-1.aliyuncs.com"
"registry.me-east-1.aliyuncs.com"
"mirror.ccs.tencentyun.com"
"gcr.io"
"asia.gcr.io"
"eu.gcr.io"
"registry.hub.docker.com"
)
## 配置需要区分公网地址和内网地址的软件源(不分地域)
# 需要同时在两个数组变量中分别定义软件源地址,并且保证排列顺序一致
# 软件源公网地址列表
mirror_list_extranet=(
"mirrors.aliyun.com/docker-ce"
"mirrors.tencent.com/docker-ce"
"mirrors.huaweicloud.com/docker-ce"
"mirrors.volces.com/docker-ce"
)
# 软件源内网地址列表
mirror_list_intranet=(
"mirrors.cloud.aliyuncs.com/docker-ce"
"mirrors.tencentyun.com/docker-ce"
"mirrors.myhuaweicloud.com/docker-ce"
"mirrors.ivolces.com/docker-ce"
)
## 赞助商广告
SPONSOR_ADS=(
"1Panel · Linux 面板|极简运维 ➜ \033[3mhttps://1panel.cn\033[0m"
"多途云 · 智能化防护,每一次连接皆在安全之下 ➜ \033[3mhttps://www.duotuyun.com\033[0m"
"毫秒镜像 · 专为中国开发者提供Docker镜像加速下载服务 ➜ \033[3mhttps://1ms.run\033[0m"
"不死鸟CDN · 香港日本高防CDN免实名/免备案轻松阻断DDOS/CC攻击 ➜ \033[3mhttps://www.bsncdn.org\033[0m"
"青叶云 · 香港1T高防自助防火墙无视CC大带宽回国优化线路 ➜ \033[3mhttps://www.qingyeyun.com\033[0m"
"莱卡云 · 专业云计算服务器提供商 ➜ \033[3m https://www.lcayun.com\033[0m"
"云悠YUNYOO · 全球高性价比云服务器低至15.99元起 ➜ \033[3mhttps://yunyoo.cc\033[0m"
"速拓云 · 国内高防云28元/月香港云100M优化线路9元/月 ➜ \033[3mhttps://www.sutuoyun.com\033[0m"
"林枫云 · 专注独立IP高频VPSR9/i9系列定制 ➜ \033[3mhttps://www.dkdun.cn\033[0m"
"语鹿云盾 · 专业CDN加速、防御亚太百兆三网优化CDN低至9元起 ➜ \033[3mhttps://www.lucdn.cn\033[0m"
"不二云 · 国内外建站快响应服务器的不二之选 ➜ \033[3mhttps://cb2.cn\033[0m"
"HKGserver · 全球家宽双ISP住宅原生云服务器54元/月起 ➜ \033[3mhttps://www.hkgserver.com\033[0m"
"浪浪云 · BGP网络让每一次连接都纵享丝滑明码标价、无套路续费 ➜ \033[3mhttps://langlangy.cn\033[0m"
)
##############################################################################
## 定义系统判定变量
SYSTEM_DEBIAN="Debian"
SYSTEM_UBUNTU="Ubuntu"
SYSTEM_KALI="Kali"
SYSTEM_DEEPIN="Deepin"
SYSTEM_LINUX_MINT="Linuxmint"
SYSTEM_ZORIN="Zorin"
SYSTEM_RASPBERRY_PI_OS="Raspberry Pi OS"
SYSTEM_REDHAT="RedHat"
SYSTEM_RHEL="Red Hat Enterprise Linux"
SYSTEM_CENTOS="CentOS"
SYSTEM_CENTOS_STREAM="CentOS Stream"
SYSTEM_ROCKY="Rocky"
SYSTEM_ALMALINUX="AlmaLinux"
SYSTEM_FEDORA="Fedora"
SYSTEM_ORACLE="Oracle Linux"
SYSTEM_OPENCLOUDOS="OpenCloudOS"
SYSTEM_OPENCLOUDOS_STREAM="OpenCloudOS Stream"
SYSTEM_TENCENTOS="TencentOS"
SYSTEM_OPENEULER="openEuler"
SYSTEM_ANOLISOS="Anolis"
SYSTEM_OPENKYLIN="openKylin"
SYSTEM_OPENSUSE="openSUSE"
SYSTEM_ARCH="Arch"
SYSTEM_ALPINE="Alpine"
SYSTEM_GENTOO="Gentoo"
SYSTEM_NIXOS="NixOS"
## 定义系统版本文件
File_LinuxRelease=/etc/os-release
File_RedHatRelease=/etc/redhat-release
File_DebianVersion=/etc/debian_version
File_ArmbianRelease=/etc/armbian-release
File_RaspberryPiOSRelease=/etc/rpi-issue
File_openEulerRelease=/etc/openEuler-release
File_HuaweiCloudEulerOSRelease=/etc/hce-release
File_OpenCloudOSRelease=/etc/opencloudos-release
File_TencentOSServerRelease=/etc/tlinux-release
File_AnolisOSRelease=/etc/anolis-release
File_AlibabaCloudLinuxRelease=/etc/alinux-release
File_OracleLinuxRelease=/etc/oracle-release
File_ArchLinuxRelease=/etc/arch-release
File_ManjaroRelease=/etc/manjaro-release
File_AlpineRelease=/etc/alpine-release
File_GentooRelease=/etc/gentoo-release
File_openKylinVersion=/etc/kylin-version/kylin-system-version.conf
## 定义软件源相关文件或目录
File_AptSourceList=/etc/apt/sources.list
Dir_AptAdditionalSources=/etc/apt/sources.list.d
Dir_YumRepos=/etc/yum.repos.d
## 定义 Docker 相关变量
Dir_Docker=/etc/docker
File_DockerConfig=$Dir_Docker/daemon.json
File_DockerConfigBackup=$Dir_Docker/daemon.json.bak
File_DockerVersionTmp=docker-version.txt
File_DockerCEVersionTmp=docker-ce-version.txt
File_DockerCECliVersionTmp=docker-ce-cli-version.txt
File_DockerSourceList=$Dir_AptAdditionalSources/docker.list
File_DockerRepo=$Dir_YumRepos/docker-ce.repo
## 定义颜色和样式变量
RED='\033[31m'
GREEN='\033[32m'
YELLOW='\033[33m'
BLUE='\033[34m'
PURPLE='\033[35m'
AZURE='\033[36m'
PLAIN='\033[0m'
BOLD='\033[1m'
SUCCESS="\033[1;32m✔${PLAIN}"
COMPLETE="\033[1;32m✔${PLAIN}"
WARN="\033[1;43m 警告 ${PLAIN}"
ERROR="\033[1;31m✘${PLAIN}"
FAIL="\033[1;31m✘${PLAIN}"
TIP="\033[1;44m 提示 ${PLAIN}"
WORKING="\033[1;36m◉${PLAIN}"
function main() {
permission_judgment
collect_system_info
run_start
choose_mirrors
if [[ "${ONLY_REGISTRY}" == "true" ]]; then
only_change_docker_registry_mirror
else
choose_protocol
close_firewall_service
install_dependency_packages
configure_docker_ce_mirror
install_docker_engine
change_docker_registry_mirror
check_installed_result
fi
run_end
}
function handle_command_options() {
## 判断参数
while [ $# -gt 0 ]; do
case "$1" in
## 指定 Docker CE 软件源地址
--source)
if [ "$2" ]; then
echo "$2" | grep -Eq "\(|\)|\[|\]|\{|\}"
if [ $? -eq 0 ]; then
command_error "$2" "$(msg "error.cmd.options.validAddress")"
else
SOURCE="$(echo "$2" | sed -e 's,^http[s]\?://,,g' -e 's,/$,,')"
shift
fi
else
command_error "$1" "$(msg "error.cmd.options.sourceAddress")"
fi
;;
## 指定 Docker Registry 仓库地址
--source-registry)
if [ "$2" ]; then
echo "$2" | grep -Eq "\(|\)|\[|\]|\{|\}"
if [ $? -eq 0 ]; then
command_error "$2" "$(msg "error.cmd.options.validAddress")"
else
SOURCE_REGISTRY="$(echo "$2" | sed -e 's,^http[s]\?://,,g' -e 's,/$,,')"
shift
fi
else
command_error "$1" "$(msg "error.cmd.options.registryAddress")"
fi
;;
## 指定 Docker CE 软件源仓库
--branch)
if [ "$2" ]; then
SOURCE_BRANCH="$2"
shift
else
command_error "$1" "$(msg "error.cmd.options.sourceRepository")"
fi
;;
## 指定 Docker CE 软件源仓库版本
--branch-version)
if [ "$2" ]; then
echo "$2" | grep -Eq "^[0-9]{1,2}$"
if [ $? -eq 0 ]; then
SOURCE_BRANCH_VERSION="$2"
shift
else
command_error "$2" "$(msg "error.cmd.options.validVersion")"
fi
else
command_error "$1" "$(msg "error.cmd.options.ceRepositoryVersion")"
fi
;;
## 指定 Docker Engine 安装版本
--designated-version)
if [ "$2" ]; then
echo "$2" | grep -Eq "^[0-9][0-9].[0-9]{1,2}.[0-9]{1,2}$"
if [ $? -eq 0 ]; then
DESIGNATED_DOCKER_VERSION="$2"
shift
else
command_error "$2" "$(msg "error.cmd.options.validVersion")"
fi
else
command_error "$1" "$(msg "error.cmd.options.version")"
fi
;;
## 指定 Debian 版本代号
--codename)
if [ "$2" ]; then
DEBIAN_CODENAME="$2"
shift
else
command_error "$1" "$(msg "error.cmd.options.codename")"
fi
;;
## Web 协议HTTP/HTTPS
--protocol)
if [ "$2" ]; then
case "$2" in
http | https | HTTP | HTTPS)
WEB_PROTOCOL="${2,,}"
shift
;;
*)
command_error "$2" "$(msg "error.cmd.options.protocol")"
;;
esac
else
command_error "$1" "$(msg "error.cmd.options.needProtocol")"
fi
;;
## 使用内网地址
--use-intranet-source)
if [ "$2" ]; then
case "$2" in
[Tt]rue | [Ff]alse)
USE_INTRANET_SOURCE="${2,,}"
shift
;;
*)
command_error "$2" "$(msg "error.cmd.options.needIntranetSource")"
;;
esac
else
command_error "$1" "$(msg "error.cmd.options.needIntranetSource")"
fi
;;
## 安装最新版本
--install-latest | --install-latested)
if [ "$2" ]; then
case "$2" in
[Tt]rue | [Ff]alse)
INSTALL_LATESTED_DOCKER="${2,,}"
shift
;;
*)
command_error "$2" "$(msg "error.cmd.options.needIntranetSource")"
;;
esac
else
command_error "$1" "$(msg "error.cmd.options.needIntranetSource")"
fi
;;
## 忽略覆盖备份提示
--ignore-backup-tips)
IGNORE_BACKUP_TIPS="true"
;;
## 关闭防火墙
--close-firewall)
if [ "$2" ]; then
case "$2" in
[Tt]rue | [Ff]alse)
CLOSE_FIREWALL="${2,,}"
shift
;;
*)
command_error "$2" "$(msg "error.cmd.options.needIntranetSource")"
;;
esac
else
command_error "$1" "$(msg "error.cmd.options.needIntranetSource")"
fi
;;
## 清除屏幕上的所有内容
--clean-screen)
if [ "$2" ]; then
case "$2" in
[Tt]rue | [Ff]alse)
CLEAN_SCREEN="${2,,}"
shift
;;
*)
command_error "$2" "$(msg "error.cmd.options.needIntranetSource")"
;;
esac
else
command_error "$1" "$(msg "error.cmd.options.needIntranetSource")"
fi
;;
## Locale
--lang)
if [ "$2" ]; then
local lang_norm="${2//_/-}"
lang_norm="${lang_norm,,}"
case "$lang_norm" in
zh | zh-cn | zh-hans | zh-hans-*)
init_msg_pack "zh-hans"
shift
;;
zh-hant | zh-hant-* | zh-tw | zh-hk)
init_msg_pack "zh-hant"
shift
;;
en | en-us | en-*)
init_msg_pack "en"
shift
;;
auto)
choose_display_language
shift
;;
*)
command_error "$2" "$(msg "error.cmd.options.validLangKey")"
;;
esac
else
command_error "$1" "$(msg "error.cmd.options.langKey")"
fi
;;
--zh | --zh-[Cc][Nn])
init_msg_pack "zh-hans"
;;
--en | --en-[Uu][Ss])
init_msg_pack "en"
;;
--zh-[Hh]an[st])
init_msg_pack "$1"
;;
## 仅更换镜像仓库模式
--only-registry)
ONLY_REGISTRY="true"
;;
## 纯净模式
--pure-mode)
PURE_MODE="true"
;;
## 命令帮助
--help)
echo -e "\n$(msg "commands.help" "https://github.com/SuperManito/LinuxMirrors/issues")\n"
exit
;;
*)
command_error "$1"
;;
esac
shift
done
## 设置部分功能的默认值
IGNORE_BACKUP_TIPS="${IGNORE_BACKUP_TIPS:-"false"}"
if [[ "${DESIGNATED_DOCKER_VERSION}" ]]; then
INSTALL_LATESTED_DOCKER="false"
fi
PURE_MODE="${PURE_MODE:-"false"}"
}
function run_start() {
if [ -z "${CLEAN_SCREEN}" ]; then
[[ -z "${SOURCE}" || -z "${SOURCE_REGISTRY}" ]] && clear
elif [ "${CLEAN_SCREEN}" == "true" ]; then
clear
fi
if [[ "${PURE_MODE}" == "true" ]]; then
return
fi
local system_name="${SYSTEM_PRETTY_NAME:-"${SYSTEM_NAME} ${SYSTEM_VERSION_ID}"}"
local arch="${DEVICE_ARCH}"
local date_time="$(date "+%Y-%m-%d %H:%M")"
local time_zone="$(timedatectl status 2>/dev/null | grep "Time zone" | awk -F ':' '{print$2}' | awk -F ' ' '{print$1}')"
echo -e "+-----------------------------------+"
echo -e "| \033[0;1;35;95m⡇\033[0m \033[0;1;33;93m⠄\033[0m \033[0;1;32;92m⣀⡀\033[0m \033[0;1;36;96m⡀\033[0;1;34;94m⢀\033[0m \033[0;1;35;95m⡀⢀\033[0m \033[0;1;31;91m⡷\033[0;1;33;93m⢾\033[0m \033[0;1;32;92m⠄\033[0m \033[0;1;36;96m⡀⣀\033[0m \033[0;1;34;94m⡀\033[0;1;35;95m⣀\033[0m \033[0;1;31;91m⢀⡀\033[0m \033[0;1;33;93m⡀\033[0;1;32;92m⣀\033[0m \033[0;1;36;96m⢀⣀\033[0m |"
echo -e "| \033[0;1;31;91m⠧\033[0;1;33;93m⠤\033[0m \033[0;1;32;92m⠇\033[0m \033[0;1;36;96m⠇⠸\033[0m \033[0;1;34;94m⠣\033[0;1;35;95m⠼\033[0m \033[0;1;31;91m⠜⠣\033[0m \033[0;1;33;93m⠇\033[0;1;32;92m⠸\033[0m \033[0;1;36;96m⠇\033[0m \033[0;1;34;94m⠏\033[0m \033[0;1;35;95m⠏\033[0m \033[0;1;33;93m⠣⠜\033[0m \033[0;1;32;92m⠏\033[0m \033[0;1;34;94m⠭⠕\033[0m |"
echo -e "+-----------------------------------+"
echo -e "$(msg "start.welcome")"
echo -e ''
echo -e "$(msg "start.runtimeEnv") ${BLUE}${system_name} ${arch}${PLAIN}"
echo -e "$(msg "start.dateTime") ${BLUE}${date_time} ${time_zone}${PLAIN}"
sleep 1 >/dev/null 2>&1
}
function run_end() {
if [[ "${PURE_MODE}" == "true" ]]; then
echo ''
return
fi
echo -e "\n✨ $(msg "end.moreInfo") 👉 \033[3mhttps://linuxmirrors.cn\033[0m"
if [[ "${#SPONSOR_ADS[@]}" -gt 0 ]]; then
echo -e "\n\033[2m$(msg "end.sponsorAds")\033[0m"
for ad in "${SPONSOR_ADS[@]}"; do
sleep 0.1
echo -e " \033[2m${ad}\033[0m"
done
fi
echo -e "\n\033[3;1mPowered by \033[34mLinuxMirrors\033[0m\n"
}
function output_error() {
[ "$1" ] && echo -e "\n$ERROR $1\n"
exit 1
}
function command_error() {
local tmp_text="$(msg "error.cmd.options.needConfirm")"
if [[ "${2}" ]]; then
tmp_text="$(msg "error.cmd.options.needSpecify" "${2}")"
fi
output_error "$(msg "error.cmd.options.invalid" "${BLUE}$1${PLAIN}" "${tmp_text}")"
}
function unsupport_system_error() {
if [[ "${2}" ]]; then
output_error "$(msg "error.unsupportSystem2" "${1}")\n\n${BLUE}$2${PLAIN}"
else
output_error "$(msg "error.unsupportSystem1" "${1}")"
fi
}
function input_error() {
echo -e "\n$WARN $(msg "error.input" "${1}")"
}
function command_exists() {
command -v "$@" &>/dev/null
}
function permission_judgment() {
if [ $UID -ne 0 ]; then
output_error "$(msg "error.needRoot")"
fi
}
function get_os_release_value() {
grep -E "^${1}=" $File_LinuxRelease | cut -d= -f2- | sed "s/[\'\"]//g"
}
function collect_system_info() {
if [ ! -s "${File_LinuxRelease}" ]; then
unsupport_system_error "$(msg "error.unknownSystem")"
fi
## 定义系统名称
SYSTEM_NAME="$(get_os_release_value NAME)"
SYSTEM_PRETTY_NAME="$(get_os_release_value PRETTY_NAME)"
## 定义系统版本号
SYSTEM_VERSION_ID="$(get_os_release_value VERSION_ID)"
SYSTEM_VERSION_ID_MAJOR="${SYSTEM_VERSION_ID%.*}"
SYSTEM_VERSION_ID_MINOR="${SYSTEM_VERSION_ID#*.}"
## 定义系统ID
SYSTEM_ID="$(get_os_release_value ID)"
## 判定当前系统派系
if [ -s "${File_DebianVersion}" ]; then
SYSTEM_FACTIONS="${SYSTEM_DEBIAN}"
elif [ -s "${File_RedHatRelease}" ]; then
SYSTEM_FACTIONS="${SYSTEM_REDHAT}"
elif [ -s "${File_openEulerRelease}" ] || [ -s "${File_HuaweiCloudEulerOSRelease}" ]; then
SYSTEM_FACTIONS="${SYSTEM_OPENEULER}"
elif [ -s "${File_OpenCloudOSRelease}" ]; then
SYSTEM_FACTIONS="${SYSTEM_OPENCLOUDOS}" # 自 9.0 版本起不再基于红帽
elif [ -s "${File_AnolisOSRelease}" ]; then
SYSTEM_FACTIONS="${SYSTEM_ANOLISOS}" # 自 8.8 版本起不再基于红帽
elif [ -s "${File_TencentOSServerRelease}" ]; then
SYSTEM_FACTIONS="${SYSTEM_TENCENTOS}" # 自 4 版本起不再基于红帽
elif [ -s "${File_openKylinVersion}" ]; then
[[ "${ONLY_REGISTRY}" != "true" ]] && unsupport_system_error "openKylin" "apt-get install -y docker\nsystemctl enable --now docker"
elif [ -f "${File_ArchLinuxRelease}" ]; then
[[ "${ONLY_REGISTRY}" != "true" ]] && unsupport_system_error "Arch Linux" "pacman -S docker\nsystemctl enable --now docker"
elif [ -f "${File_GentooRelease}" ]; then
[[ "${ONLY_REGISTRY}" != "true" ]] && unsupport_system_error "Gentoo"
elif [[ "${SYSTEM_NAME}" == *"openSUSE"* ]]; then
[[ "${ONLY_REGISTRY}" != "true" ]] && unsupport_system_error "openSUSE" "zypper install docker\nsystemctl enable --now docker"
elif [[ "${SYSTEM_NAME}" == *"NixOS"* ]]; then
[[ "${ONLY_REGISTRY}" != "true" ]] && unsupport_system_error "NixOS"
else
unsupport_system_error "$(msg "error.unknownSystem")"
fi
## 判定系统类型、版本、版本号
case "${SYSTEM_FACTIONS}" in
"${SYSTEM_DEBIAN}")
if command_exists lsb_release; then
SYSTEM_JUDGMENT="$(lsb_release -is)"
SYSTEM_VERSION_CODENAME="${DEBIAN_CODENAME:-"$(lsb_release -cs)"}"
else
## https://codeberg.org/gioele/lsb-release-minimal
SYSTEM_JUDGMENT="${SYSTEM_ID^}"
if [ "${SYSTEM_NAME}" ]; then
if [[ "${SYSTEM_ID,,}" == "${SYSTEM_NAME,,}" ]]; then
SYSTEM_JUDGMENT="${SYSTEM_NAME}"
fi
fi
SYSTEM_VERSION_CODENAME="${DEBIAN_CODENAME:-"$(get_os_release_value VERSION_CODENAME)"}"
fi
# Raspberry Pi OS
if [ -s "${File_RaspberryPiOSRelease}" ]; then
SYSTEM_JUDGMENT="${SYSTEM_RASPBERRY_PI_OS}"
SYSTEM_PRETTY_NAME="${SYSTEM_RASPBERRY_PI_OS}"
fi
;;
"${SYSTEM_REDHAT}")
SYSTEM_JUDGMENT="$(awk '{printf $1}' $File_RedHatRelease)"
## 特殊系统判断
# Red Hat Enterprise Linux
grep -q "${SYSTEM_RHEL}" $File_RedHatRelease && SYSTEM_JUDGMENT="${SYSTEM_RHEL}"
# 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}"
;;
esac
## 判定系统处理器架构
DEVICE_ARCH_RAW="$(uname -m)"
case "${DEVICE_ARCH_RAW}" in
x86_64)
DEVICE_ARCH="x86_64"
;;
aarch64)
DEVICE_ARCH="ARM64"
;;
armv8l)
DEVICE_ARCH="ARMv8_32"
;;
armv7l)
DEVICE_ARCH="ARMv7"
;;
armv6l)
DEVICE_ARCH="ARMv6"
;;
armv5tel)
DEVICE_ARCH="ARMv5"
;;
ppc64le)
DEVICE_ARCH="ppc64le"
;;
s390x)
DEVICE_ARCH="s390x"
;;
i386 | i686)
output_error "$(msg "error.unsupportX86_32")"
;;
*)
output_error "$(msg "error.unknownArch" "${DEVICE_ARCH_RAW}")"
;;
esac
## 定义软件源仓库名称
if [[ -z "${SOURCE_BRANCH}" ]]; then
case "${SYSTEM_FACTIONS}" in
"${SYSTEM_DEBIAN}")
case "${SYSTEM_JUDGMENT}" in
"${SYSTEM_DEBIAN}")
SOURCE_BRANCH="debian"
;;
"${SYSTEM_UBUNTU}" | "${SYSTEM_ZORIN}")
SOURCE_BRANCH="ubuntu"
;;
"${SYSTEM_KALI}")
SOURCE_BRANCH="debian"
SOURCE_BRANCH_CODENAME="trixie"
;;
"${SYSTEM_LINUX_MINT}")
if [[ "${SYSTEM_NAME}" == *"LMDE"* ]]; then
SOURCE_BRANCH="debian"
SOURCE_BRANCH_CODENAME="$(get_os_release_value DEBIAN_CODENAME)"
else
SOURCE_BRANCH="ubuntu"
SOURCE_BRANCH_CODENAME="$(get_os_release_value UBUNTU_CODENAME)"
fi
if [[ -z "${SOURCE_BRANCH_CODENAME}" ]]; then
SOURCE_BRANCH="debian"
SOURCE_BRANCH_CODENAME="bookworm"
fi
;;
"${SYSTEM_RASPBERRY_PI_OS}")
case "${DEVICE_ARCH_RAW}" in
x86_64 | aarch64)
SOURCE_BRANCH="debian"
;;
*)
# 注:自 Docker 29 版本起将不再提供此分支仓库
SOURCE_BRANCH="raspbian"
;;
esac
;;
*)
# 其余 Debian 系衍生操作系统
SOURCE_BRANCH="debian"
SOURCE_BRANCH_CODENAME="bookworm"
;;
esac
;;
"${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}" | "${SYSTEM_TENCENTOS}")
case "${SYSTEM_JUDGMENT}" in
"${SYSTEM_FEDORA}")
SOURCE_BRANCH="fedora"
;;
"${SYSTEM_RHEL}")
SOURCE_BRANCH="rhel"
;;
*)
SOURCE_BRANCH="centos"
;;
esac
if [[ "${DEVICE_ARCH_RAW}" == "s390x" ]]; then
output_error "$(msg "error.unsupportS390x")"
fi
;;
esac
fi
## 定义软件源更新文字
case "${SYSTEM_FACTIONS}" in
"${SYSTEM_DEBIAN}")
SYNC_MIRROR_TEXT="$(msg "source.sync.text1")"
;;
"${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}" | "${SYSTEM_TENCENTOS}")
SYNC_MIRROR_TEXT="$(msg "source.sync.text2")"
;;
esac
## 判断是否可以使用高级交互式选择器
CAN_USE_ADVANCED_INTERACTIVE_SELECTION="false"
if command_exists tput; then
CAN_USE_ADVANCED_INTERACTIVE_SELECTION="true"
fi
}
## 选择显示语言
function choose_display_language() {
local result
if command_exists tput; then
local lang_key_labels=()
local language_text
for ((i = 0; i < ${#MESSAGE_LANG_KEYS[@]}; i++)); do
language_text="${MESSAGE_LANG_DISPLAY[${MESSAGE_LANG_KEYS[$i]}]}"
if [[ "${language_text}" ]]; then
lang_key_labels+=("${language_text}")
else
lang_key_labels+=("")
fi
done
interactive_select_list "MESSAGE_LANG_KEYS" "\n ${BOLD}Please select the display language:${PLAIN}\n" "lang_key_labels"
result="${_SELECT_RESULT%%@@*}"
else
echo ''
for ((i = 0; i < ${#MESSAGE_LANG_KEYS[@]}; i++)); do
echo -e " $((i + 1)). ${MESSAGE_LANG_DISPLAY[${MESSAGE_LANG_KEYS[$i]}]}"
done
local CHOICE="$(echo -e "\n${BOLD}└─ Please select and enter the display language [ 1-${#MESSAGE_LANG_KEYS[@]} ]${PLAIN}")"
while true; do
read -rp "${CHOICE}" INPUT
case "${INPUT}" in
[1-9] | [1-9][0-9])
local tmp_result="${MESSAGE_LANG_KEYS[$((INPUT - 1))]}"
if [[ -z "${tmp_result}" ]]; then
echo -e "\n$WARN $(msg "warn.needValidNumberIndex")"
else
result="${tmp_result}"
break
fi
;;
*)
echo -e "\n$WARN $(msg "warn.needInputNumberIndex")"
;;
esac
done
fi
init_msg_pack "${result}"
}
function choose_mirrors() {
## 打印软件源列表
function print_mirrors_list() {
local tmp_name tmp_index i j
function StringLength() {
local text=$1
echo "${#text}"
}
local list_arr=()
local list_arr_sum="$(eval echo \${#$1[@]})"
for ((i = 0; i < $list_arr_sum; i++)); do
list_arr[$i]="$(eval echo \${$1[i]})"
done
local name_width=${2:-"30"}
local list_labels=()
if [[ "${3}" ]]; then
eval "list_labels=(\"\${${3}[@]}\")"
fi
if command_exists printf; then
local tmp_uchar_1 tmp_uchar_2 tmp_uchar_3 tmp_uchar_4 tmp_default_length tmp_length tmp_unicode_length tmp_spaces_nums tmp_max_length
for ((i = 0; i < ${#list_arr[@]}; i++)); do
if [[ "${list_labels[$i]}" ]]; then
tmp_name="${list_labels[$i]}"
else
tmp_name="${list_arr[i]}"
fi
tmp_index=$((i + 1))
tmp_default_length="${name_width}"
tmp_uchar_1=$(echo "${tmp_name}" | grep -c "“")
tmp_uchar_2=$(echo "${tmp_name}" | grep -c "”")
tmp_uchar_3=$(echo "${tmp_name}" | grep -c "")
tmp_uchar_4=$(echo "${tmp_name}" | grep -c "")
[[ "${tmp_uchar_1}" -gt 0 ]] && ((tmp_default_length += tmp_uchar_1))
[[ "${tmp_uchar_2}" -gt 0 ]] && ((tmp_default_length += tmp_uchar_2))
[[ "${tmp_uchar_3}" -gt 0 ]] && ((tmp_default_length += tmp_uchar_3))
[[ "${tmp_uchar_4}" -gt 0 ]] && ((tmp_default_length += tmp_uchar_4))
tmp_length=$(StringLength "${tmp_name}")
tmp_unicode_length=$(StringLength "$(echo "${tmp_name}" | sed "s|[0-9a-zA-Z -~]||g; s| ||g")")
tmp_max_length=$((tmp_default_length + tmp_unicode_length))
tmp_spaces_nums=$((((tmp_default_length - tmp_unicode_length - tmp_length)) / 2))
if [[ $tmp_spaces_nums -gt 0 ]]; then
tmp_name="${tmp_name}$(printf '%*s' ${tmp_spaces_nums} '')"
fi
printf "❖ %-${tmp_max_length}s %4s\n" "${tmp_name}" "${tmp_index})"
done
else
for ((i = 0; i < ${#list_arr[@]}; i++)); do
if [[ "${list_labels[$i]}" ]]; then
tmp_name="${list_labels[$i]}"
else
tmp_name="${list_arr[i]}"
fi
tmp_index=$((i + 1))
echo -e "$tmp_index. ${tmp_name}"
done
fi
}
## 选择使用软件源内网地址
function choose_use_intranet_address() {
local intranet_source
for ((i = 0; i < ${#mirror_list_extranet[@]}; i++)); do
if [[ "${SOURCE}" == "${mirror_list_extranet[i]}" ]]; then
intranet_source="${mirror_list_intranet[i]}"
break
else
continue
fi
done
if [[ -z "${USE_INTRANET_SOURCE}" ]]; then
if [[ "${CAN_USE_ADVANCED_INTERACTIVE_SELECTION}" == "true" ]]; then
echo ''
interactive_select_boolean "${BOLD}$(msg "interaction.source.type.select")${PLAIN}" "$(msg "interaction.source.type.public")" "$(msg "interaction.source.type.intranet")"
if [[ "${_SELECT_RESULT}" == "false" ]]; then
SOURCE="${intranet_source}"
ONLY_HTTP="true" # 强制使用 HTTP 协议
[[ "${PURE_MODE}" != "true" ]] && echo -e "\n$WARN $(msg "warn.usedIntranetSource")"
fi
else
local CHOICE="$(echo -e "\n${BOLD}└─ $(msg "interaction.source.type.usePublicAddress")? [Y/n] ${PLAIN}")"
read -rp "${CHOICE}" INPUT
[[ -z "${INPUT}" ]] && INPUT=Y
case "${INPUT}" in
[Yy] | [Yy][Ee][Ss]) ;;
[Nn] | [Nn][Oo])
SOURCE="${intranet_source}"
ONLY_HTTP="true" # 强制使用 HTTP 协议
[[ "${PURE_MODE}" != "true" ]] && echo -e "\n$WARN $(msg "warn.usedIntranetSource")"
;;
*)
input_error "$(msg "error.defaultBehavior.noUseIntranetSource")"
;;
esac
fi
elif [[ "${USE_INTRANET_SOURCE}" == "true" ]]; then
SOURCE="${intranet_source}"
fi
}
local mirror_list_name mirror_list_length
local mirror_list_labels=()
local label_msg_index label_msg_content
if [[ -z "${SOURCE}" ]] && [[ "${ONLY_REGISTRY}" != "true" ]]; then
mirror_list_name="mirror_list_docker_ce"
mirror_list_length=$(eval "echo \${#${mirror_list_name}[@]}")
for ((i = 0; i < ${mirror_list_length}; i++)); do
label_msg_index="mirrors.dockerCE.${i}"
label_msg_content="$(msg "${label_msg_index}")"
if [[ "${label_msg_content}" == "${label_msg_index}" ]]; then
mirror_list_labels+=("")
else
mirror_list_labels+=("${label_msg_content}")
fi
done
if [[ "${CAN_USE_ADVANCED_INTERACTIVE_SELECTION}" == "true" ]]; then
interactive_select_list "${mirror_list_name}" "\n ${BOLD}$(msg "interaction.source.dockerCE.select")${PLAIN}\n" "mirror_list_labels"
SOURCE="${_SELECT_RESULT%%@@*}"
echo -e "\n${GREEN}${PLAIN} ${BOLD}Docker CE: ${_SELECT_RESULT#*@@}${PLAIN}"
else
echo ''
print_mirrors_list "${mirror_list_name}" 39 "mirror_list_labels"
local CHOICE_B="$(echo -e "\n${BOLD}└─ $(msg "interaction.source.dockerCE.selectAndInput") [ 1-$(eval echo \${#${mirror_list_name}[@]}) ]${PLAIN}")"
while true; do
read -rp "${CHOICE_B}" INPUT
case "${INPUT}" in
[1-9] | [1-9][0-9] | [1-9][0-9][0-9])
local tmp_result="$(eval echo \${${mirror_list_name}[$((INPUT - 1))]})"
if [[ -z "${tmp_result}" ]]; then
echo -e "\n$WARN $(msg "warn.needValidNumberIndex")"
else
SOURCE="$(echo "${tmp_result}" | awk -F '@' '{print$2}')"
break
fi
;;
*)
echo -e "\n$WARN $(msg "warn.needInputNumberIndex")"
;;
esac
done
fi
fi
## 选择软件源内网地址
if [[ "${mirror_list_extranet[*]}" =~ (^|[^[:alpha:]])"${SOURCE}"([^[:alpha:]]|$) ]]; then
choose_use_intranet_address
fi
if [[ -z "${SOURCE_REGISTRY}" ]]; then
mirror_list_name="mirror_list_registry"
mirror_list_labels=()
mirror_list_length=$(eval "echo \${#${mirror_list_name}[@]}")
for ((i = 0; i < ${mirror_list_length}; i++)); do
label_msg_index="mirrors.registry.${i}"
label_msg_content="$(msg "${label_msg_index}")"
if [[ "${label_msg_content}" == "${label_msg_index}" ]]; then
mirror_list_labels+=("")
else
mirror_list_labels+=("${label_msg_content}")
fi
done
if [[ "${CAN_USE_ADVANCED_INTERACTIVE_SELECTION}" == "true" ]]; then
sleep 1 >/dev/null 2>&1
interactive_select_list "${mirror_list_name}" "\n ${BOLD}$(msg "interaction.source.dockerRegistry.select")${PLAIN}\n" "mirror_list_labels"
SOURCE_REGISTRY="${_SELECT_RESULT%%@@*}"
echo -e "\n${GREEN}${PLAIN} ${BOLD}Docker Registry: $(echo "${_SELECT_RESULT#*@@}" | sed 's|(推荐)||g; s|(推薦)||g')${PLAIN}"
else
echo ''
print_mirrors_list "${mirror_list_name}" 45 "mirror_list_labels"
local CHOICE_C="$(echo -e "\n${BOLD}└─ $(msg "interaction.source.dockerRegistry.selectAndInput") [ 1-$(eval echo \${#${mirror_list_name}[@]}) ]${PLAIN}")"
while true; do
read -rp "${CHOICE_C}" INPUT
case "${INPUT}" in
[1-9] | [1-9][0-9] | [1-9][0-9][0-9])
local tmp_source="$(eval echo \${${mirror_list_name}[$(($INPUT - 1))]})"
if [[ -z "${tmp_source}" ]]; then
echo -e "\n$WARN $(msg "warn.needValidNumberIndex")"
else
SOURCE_REGISTRY="$(eval echo \${${mirror_list_name}[$(($INPUT - 1))]} | awk -F '@' '{print$2}')"
break
fi
;;
*)
echo -e "\n$WARN $(msg "warn.needInputNumberIndex")"
;;
esac
done
fi
fi
}
## 选择同步或更新软件源所使用的 Web 协议( HTTP/HTTPS
function choose_protocol() {
if [[ -z "${WEB_PROTOCOL}" ]]; then
if [[ "${ONLY_HTTP}" == "true" ]]; then
WEB_PROTOCOL="http"
else
if [[ "${CAN_USE_ADVANCED_INTERACTIVE_SELECTION}" == "true" ]]; then
echo ''
interactive_select_boolean "${BOLD}$(msg "interaction.protocol.select")${PLAIN}" "HTTP" "HTTPS"
if [[ "${_SELECT_RESULT}" == "true" ]]; then
WEB_PROTOCOL="http"
else
WEB_PROTOCOL="https"
fi
else
local CHOICE="$(echo -e "\n${BOLD}└─ $(msg "interaction.protocol.useHttp")? [Y/n] ${PLAIN}")"
read -rp "${CHOICE}" INPUT
[[ -z "${INPUT}" ]] && INPUT=Y
case "${INPUT}" in
[Yy] | [Yy][Ee][Ss])
WEB_PROTOCOL="http"
;;
[Nn] | [Nn][Oo])
WEB_PROTOCOL="https"
;;
*)
input_error "$(msg "error.defaultBehavior.https")"
WEB_PROTOCOL="https"
;;
esac
fi
fi
fi
WEB_PROTOCOL="${WEB_PROTOCOL,,}"
}
## 关闭防火墙和SELinux
function close_firewall_service() {
if ! command_exists systemctl; then
return
fi
if [[ "$(systemctl is-active firewalld)" == "active" ]]; then
if [[ -z "${CLOSE_FIREWALL}" ]]; then
local ask_text="$(msg "interaction.firewall.close")?"
if [[ "${CAN_USE_ADVANCED_INTERACTIVE_SELECTION}" == "true" ]]; then
echo ''
interactive_select_boolean "${BOLD}${ask_text}${PLAIN}"
if [[ "${_SELECT_RESULT}" == "true" ]]; then
CLOSE_FIREWALL="true"
fi
else
local CHOICE="$(echo -e "\n${BOLD}└─ ${ask_text} [Y/n] ${PLAIN}")"
read -rp "${CHOICE}" INPUT
[[ -z "${INPUT}" ]] && INPUT=Y
case "${INPUT}" in
[Yy] | [Yy][Ee][Ss])
CLOSE_FIREWALL="true"
;;
[Nn] | [Nn][Oo]) ;;
*)
input_error "$(msg "error.defaultBehavior.noClose")"
;;
esac
fi
fi
if [[ "${CLOSE_FIREWALL}" == "true" ]]; then
local SelinuxConfig=/etc/selinux/config
systemctl disable --now firewalld >/dev/null 2>&1
[ -s "${SelinuxConfig}" ] && sed -i "s/SELINUX=enforcing/SELINUX=disabled/g" $SelinuxConfig && setenforce 0 >/dev/null 2>&1
fi
fi
}
## 安装环境包
function install_dependency_packages() {
local commands package_manager
## 删除原有源
case "${SYSTEM_FACTIONS}" in
"${SYSTEM_DEBIAN}")
sed -i '/docker-ce/d' $File_AptSourceList
rm -rf $File_DockerSourceList
;;
"${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}" | "${SYSTEM_TENCENTOS}")
rm -rf $Dir_YumRepos/*docker*.repo
;;
esac
## 更新软件源
commands=()
case "${SYSTEM_FACTIONS}" in
"${SYSTEM_DEBIAN}")
package_manager="apt-get"
commands+=("${package_manager} update")
;;
"${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}" | "${SYSTEM_TENCENTOS}")
package_manager="$(get_package_manager)"
commands+=("${package_manager} makecache")
;;
esac
if [[ "${PURE_MODE}" == "true" ]]; then
local exec_cmd=""
for cmd in "${commands[@]}"; do
if [[ -z "${exec_cmd}" ]]; then
exec_cmd="${cmd}"
else
exec_cmd="${exec_cmd} ; ${cmd}"
fi
done
echo ''
animate_exec "${exec_cmd}" "${SYNC_MIRROR_TEXT}"
else
echo ''
for cmd in "${commands[@]}"; do
eval "${cmd}"
done
fi
if [ $? -ne 0 ]; then
output_error "$(msg "error.sync" "${SYNC_MIRROR_TEXT}" "${BLUE}${package_manager}${PLAIN}")"
fi
commands=()
case "${SYSTEM_FACTIONS}" in
"${SYSTEM_DEBIAN}")
commands+=("${package_manager} install -y ca-certificates curl")
;;
"${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}" | "${SYSTEM_TENCENTOS}")
# 注:红帽 8 版本才发布了 dnf 包管理工具
case "${SYSTEM_VERSION_ID_MAJOR}" in
7)
commands+=("${package_manager} install -y yum-utils device-mapper-persistent-data lvm2")
;;
*)
if [[ "${package_manager}" == "dnf" ]]; then
commands+=("${package_manager} install -y dnf-plugins-core")
else
commands+=("${package_manager} install -y yum-utils device-mapper-persistent-data lvm2")
fi
;;
esac
;;
esac
if [[ "${PURE_MODE}" == "true" ]]; then
local exec_cmd=""
for cmd in "${commands[@]}"; do
if [[ -z "${exec_cmd}" ]]; then
exec_cmd="${cmd}"
else
exec_cmd="${exec_cmd} ; ${cmd}"
fi
done
echo ''
animate_exec "${exec_cmd}" "$(msg "work.installDependents")"
else
for cmd in "${commands[@]}"; do
eval "${cmd}"
done
fi
}
## 配置 Docker CE 源
function configure_docker_ce_mirror() {
local commands=()
case "${SYSTEM_FACTIONS}" in
"${SYSTEM_DEBIAN}")
## 处理 GPG 密钥
local file_keyring="/etc/apt/keyrings/docker.asc"
apt-key del 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88 >/dev/null 2>&1 # 删除旧的密钥
[ -f "${file_keyring}" ] && rm -rf $file_keyring
install -m 0755 -d /etc/apt/keyrings
curl -fsSL "${WEB_PROTOCOL}://${SOURCE}/linux/${SOURCE_BRANCH}/gpg" -o $file_keyring >/dev/null
if [ $? -ne 0 ]; then
output_error "$(msg "error.downloadGPG")"
fi
chmod a+r $file_keyring
## 添加源
[ -d "${Dir_AptAdditionalSources}" ] || mkdir -p $Dir_AptAdditionalSources
local source_content="deb [arch=$(dpkg --print-architecture) signed-by=${file_keyring}] ${WEB_PROTOCOL}://${SOURCE}/linux/${SOURCE_BRANCH} ${SOURCE_BRANCH_CODENAME:-"${SYSTEM_VERSION_CODENAME}"} stable"
echo "${source_content}" | tee $File_DockerSourceList >/dev/null 2>&1
commands+=("apt-get update")
;;
"${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}" | "${SYSTEM_TENCENTOS}")
local repo_file_url="${WEB_PROTOCOL}://${SOURCE}/linux/${SOURCE_BRANCH}/docker-ce.repo"
local package_manager="$(get_package_manager)"
case "${SYSTEM_VERSION_ID_MAJOR}" in
7)
yum-config-manager -y --add-repo "${repo_file_url}"
;;
*)
if [[ "${SYSTEM_JUDGMENT}" == "${SYSTEM_FEDORA}" ]]; then
dnf-3 config-manager -y --add-repo "${repo_file_url}"
else
if [[ "${package_manager}" == "dnf" ]]; then
dnf config-manager -y --add-repo "${repo_file_url}"
else
yum-config-manager -y --add-repo "${repo_file_url}"
fi
fi
;;
esac
sed -e "s|https://download.docker.com|${WEB_PROTOCOL}://${SOURCE}|g" \
-e "s|http[s]\?://.*/linux/${SOURCE_BRANCH}/|${WEB_PROTOCOL}://${SOURCE}/linux/${SOURCE_BRANCH}/|g" \
-i \
$File_DockerRepo
## 处理版本号
if [[ "${SOURCE_BRANCH_VERSION}" ]]; then
# 指定版本
sed -e "s|\$releasever|${SOURCE_BRANCH_VERSION}|g" \
-i \
$File_DockerRepo
commands+=("${package_manager} makecache")
elif [[ "${SYSTEM_JUDGMENT}" != "${SYSTEM_FEDORA}" ]]; then
# 兼容处理
local target_version
case "${SYSTEM_VERSION_ID_MAJOR}" in
7 | 8 | 9 | 10)
target_version="${SYSTEM_VERSION_ID_MAJOR}"
;;
*)
target_version="8" # 注部分系统使用9版本分支会有兼容性问题
## 适配国产操作系统
# OpenCloudOS、Anolis OS 的 23 版本
if [[ "${SYSTEM_JUDGMENT}" == "${SYSTEM_OPENCLOUDOS}" || "${SYSTEM_JUDGMENT}" == "${SYSTEM_ANOLISOS}" ]]; then
if [[ "${SYSTEM_VERSION_ID_MAJOR}" == 23 ]]; then
target_version="9"
fi
fi
if [[ "${SYSTEM_JUDGMENT}" == "${SYSTEM_OPENEULER}" ]]; then
if [ -s "${File_HuaweiCloudEulerOSRelease}" ]; then
# Huawei Cloud EulerOS
case "${SYSTEM_VERSION_ID_MAJOR}" in
1)
target_version="8" # openEuler 20
;;
2)
target_version="9" # openEuler 22
;;
esac
else
# openEuler
if [[ "${SYSTEM_VERSION_ID_MAJOR}" -ge 22 ]]; then
target_version="9"
fi
fi
fi
# TencentOS Server
if [ -s "${File_TencentOSServerRelease}" ]; then
case "${SYSTEM_VERSION_ID_MAJOR}" in
4)
target_version="9"
;;
3)
target_version="8"
;;
2)
target_version="7"
;;
esac
fi
# Alibaba Cloud Linux
if [ -s "${File_AnolisOSRelease}" ] && [ -s "${File_AlibabaCloudLinuxRelease}" ]; then
case "${SYSTEM_VERSION_ID_MAJOR}" in
3)
target_version="8"
;;
2)
target_version="7"
;;
esac
fi
;;
esac
sed -e "s|\$releasever|${target_version}|g" \
-i \
$File_DockerRepo
commands+=("${package_manager} makecache")
fi
;;
esac
if [[ "${PURE_MODE}" == "true" ]]; then
local exec_cmd=""
for cmd in "${commands[@]}"; do
if [[ -z "${exec_cmd}" ]]; then
exec_cmd="${cmd}"
else
exec_cmd="${exec_cmd} ; ${cmd}"
fi
done
echo ''
animate_exec "${exec_cmd}" "${SYNC_MIRROR_TEXT}"
else
for cmd in "${commands[@]}"; do
eval "${cmd}"
done
fi
}
## 安装 Docker Engine
function install_docker_engine() {
## 导出可安装的版本列表
function export_version_list() {
case "${SYSTEM_FACTIONS}" in
"${SYSTEM_DEBIAN}")
apt-cache madison docker-ce | awk '{print $3}' | grep -Eo "[0-9][0-9].[0-9]{1,2}.[0-9]{1,2}" >$File_DockerCEVersionTmp
apt-cache madison docker-ce-cli | awk '{print $3}' | grep -Eo "[0-9][0-9].[0-9]{1,2}.[0-9]{1,2}" >$File_DockerCECliVersionTmp
grep -wf $File_DockerCEVersionTmp $File_DockerCECliVersionTmp >$File_DockerVersionTmp
;;
"${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}" | "${SYSTEM_TENCENTOS}")
local package_manager="$(get_package_manager)"
$package_manager list docker-ce --showduplicates | sort -r | awk '{print $2}' | grep -Eo "[0-9][0-9].[0-9]{1,2}.[0-9]{1,2}" >$File_DockerCEVersionTmp
$package_manager list docker-ce-cli --showduplicates | sort -r | awk '{print $2}' | grep -Eo "[0-9][0-9].[0-9]{1,2}.[0-9]{1,2}" >$File_DockerCECliVersionTmp
grep -wf $File_DockerCEVersionTmp $File_DockerCECliVersionTmp >$File_DockerVersionTmp
;;
esac
rm -rf $File_DockerCEVersionTmp $File_DockerCECliVersionTmp
}
## 卸载 Docker Engine 原有版本软件包
function uninstall_original_version() {
if command_exists docker; then
# 先停止并禁用 Docker 服务
systemctl disable --now docker >/dev/null 2>&1
sleep 2s
fi
# 确定需要卸载的软件包
local package_list
case "${SYSTEM_FACTIONS}" in
"${SYSTEM_DEBIAN}")
package_list='docker* podman podman-docker containerd runc'
;;
"${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}" | "${SYSTEM_TENCENTOS}")
package_list='docker* podman podman-docker runc'
;;
esac
# 卸载软件包并清理残留
case "${SYSTEM_FACTIONS}" in
"${SYSTEM_DEBIAN}")
apt-get remove -y $package_list >/dev/null 2>&1
apt-get autoremove -y >/dev/null 2>&1
;;
"${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}" | "${SYSTEM_TENCENTOS}")
local package_manager="$(get_package_manager)"
$package_manager remove -y $package_list >/dev/null 2>&1
$package_manager autoremove -y >/dev/null 2>&1
;;
esac
}
## 安装
function install_main() {
local target_docker_version
local pkgs=""
local commands=()
if [[ "${INSTALL_LATESTED_DOCKER}" == "true" ]]; then
pkgs="docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin"
else
export_version_list
if [ ! -s "${File_DockerVersionTmp}" ]; then
rm -rf $File_DockerVersionTmp
output_error "$(msg "error.queryVersionFailed")"
fi
if [[ "${DESIGNATED_DOCKER_VERSION}" ]]; then
cat $File_DockerVersionTmp | grep -Eq "^${DESIGNATED_DOCKER_VERSION}$"
if [ $? -ne 0 ]; then
rm -rf $File_DockerVersionTmp
output_error "$(msg "error.designatedVersion")"
fi
target_docker_version="${DESIGNATED_DOCKER_VERSION}"
else
if [[ "${CAN_USE_ADVANCED_INTERACTIVE_SELECTION}" == "true" ]]; then
local version_list=(
$(cat $File_DockerVersionTmp | sort -t '.' -k1,1nr -k2,2nr -k3,3nr | tr '\n' ' ' | sed 's/ $//')
)
local mirror_list_name="version_list"
interactive_select_list "${mirror_list_name}" "\n ${BOLD}$(msg "interaction.install.selectVersion")${PLAIN}\n"
target_docker_version="${_SELECT_RESULT}"
echo -e "\n${GREEN}${PLAIN} ${BOLD}$(msg "interaction.install.selectedVersion")${target_docker_version}${PLAIN}\n"
else
echo -e "\n${GREEN} --------- $(msg "interaction.install.selectedTitle" "28.3.0") ---------- ${PLAIN}\n"
cat $File_DockerVersionTmp
while true; do
local CHOICE="$(echo -e "\n${BOLD}└─ $(msg "interaction.install.inputVersion")${PLAIN}\n")"
read -rp "${CHOICE}" target_docker_version
echo ''
cat $File_DockerVersionTmp | grep -Eqw "${target_docker_version}"
if [ $? -eq 0 ]; then
echo "${target_docker_version}" | grep -Eqw '[0-9][0-9].[0-9]{1,2}.[0-9]{1,2}'
if [ $? -eq 0 ]; then
break
else
echo -e "$ERROR $(msg "error.invalidVersion")"
fi
else
echo -e "$ERROR $(msg "error.reEnter")"
fi
done
fi
fi
rm -rf $File_DockerVersionTmp
local major_version="$(echo ${target_docker_version} | cut -d'.' -f1)"
local minor_version="$(echo ${target_docker_version} | cut -d'.' -f2)"
case "${SYSTEM_FACTIONS}" in
"${SYSTEM_DEBIAN}")
if [[ $major_version -gt 18 ]] || [[ $major_version -eq 18 && $minor_version -ge 9 ]]; then
local tmp_version="$(apt-cache madison docker-ce-cli | grep "${target_docker_version}" | head -1 | awk '{print $3}' | awk -F "${target_docker_version}" '{print$1}')"
pkgs="docker-ce=${tmp_version}${target_docker_version}* docker-ce-cli=${tmp_version}${target_docker_version}*"
else
pkgs="docker-ce=${target_docker_version}* docker-ce-cli=${target_docker_version}*"
fi
;;
"${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}" | "${SYSTEM_TENCENTOS}")
pkgs="docker-ce-${target_docker_version}"
if [[ $major_version -gt 18 ]] || [[ $major_version -eq 18 && $minor_version -ge 9 ]]; then
pkgs="${pkgs} docker-ce-cli-${target_docker_version}"
fi
;;
esac
pkgs="${pkgs} containerd.io"
if [[ $major_version -gt 20 ]] || [[ $major_version -eq 20 && $minor_version -ge 10 ]]; then
pkgs="${pkgs} docker-compose-plugin"
fi
if [[ $major_version -ge 23 ]]; then
pkgs="${pkgs} docker-buildx-plugin"
fi
fi
case "${SYSTEM_FACTIONS}" in
"${SYSTEM_DEBIAN}")
commands+=("apt-get install -y ${pkgs}")
;;
"${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}" | "${SYSTEM_TENCENTOS}")
commands+=("$(get_package_manager) install -y ${pkgs}")
;;
esac
echo ''
if [[ "${PURE_MODE}" == "true" ]]; then
local exec_cmd=""
for cmd in "${commands[@]}"; do
if [[ -z "${exec_cmd}" ]]; then
exec_cmd="${cmd}"
else
exec_cmd="${exec_cmd} ; ${cmd}"
fi
done
animate_exec "${exec_cmd}" "$(msg "work.installDockerEngine")"
else
for cmd in "${commands[@]}"; do
eval "${cmd}"
done
fi
[ $? -ne 0 ] && output_error "$(msg "error.installDockerEngineFailed")"
}
## 判断是否手动选择安装版本
if [[ -z "${INSTALL_LATESTED_DOCKER}" ]]; then
local ask_text="$(msg "interaction.install.latestVersion")?"
if [[ "${CAN_USE_ADVANCED_INTERACTIVE_SELECTION}" == "true" ]]; then
echo ''
interactive_select_boolean "${BOLD}${ask_text}${PLAIN}"
if [[ "${_SELECT_RESULT}" == "true" ]]; then
INSTALL_LATESTED_DOCKER="true"
else
INSTALL_LATESTED_DOCKER="false"
fi
else
local CHOICE_A="$(echo -e "\n${BOLD}└─ ${ask_text} [Y/n] ${PLAIN}")"
read -rp "${CHOICE_A}" INPUT
[[ -z "${INPUT}" ]] && INPUT=Y
case $INPUT in
[Yy] | [Yy][Ee][Ss])
INSTALL_LATESTED_DOCKER="true"
;;
[Nn] | [Nn][Oo])
INSTALL_LATESTED_DOCKER="false"
;;
*)
INSTALL_LATESTED_DOCKER="true"
input_error "$(msg "error.defaultBehavior.installLatest")"
;;
esac
fi
fi
## 判定是否已安装
case "${SYSTEM_FACTIONS}" in
"${SYSTEM_DEBIAN}")
dpkg -l | grep docker-ce-cli -q
;;
"${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}" | "${SYSTEM_TENCENTOS}")
rpm -qa | grep docker-ce-cli -q
;;
esac
if [ $? -eq 0 ]; then
export_version_list
local current_docker_version="$(docker -v | grep -Eo "[0-9][0-9]\.[0-9]{1,2}\.[0-9]{1,2}")"
local latest_docker_version="$(cat $File_DockerVersionTmp | head -n 1)"
rm -rf $File_DockerVersionTmp
if [[ "${current_docker_version}" == "${latest_docker_version}" ]] && [[ "${INSTALL_LATESTED_DOCKER}" == "true" ]]; then
echo -e "\n$TIP $(msg "tip.skipInstallDockerEngine")"
else
uninstall_original_version
install_main
fi
else
uninstall_original_version
install_main
fi
}
## 修改 Docker Registry 镜像仓库源
function change_docker_registry_mirror() {
## 使用官方 Docker Hub
if [[ "${REGISTRY_SOURCEL}" == "registry.hub.docker.com" ]]; then
if [ -s "${File_DockerConfig}" ]; then
## 安装 jq
local package_manager="$(get_package_manager)"
$package_manager install -y jq
if command_exists jq; then
jq 'del(.["registry-mirrors"])' $File_DockerConfig >$File_DockerConfig.tmp && mv $File_DockerConfig.tmp $File_DockerConfig
# 重启服务
systemctl daemon-reload
if [[ "$(systemctl is-active docker 2>/dev/null)" == "active" ]]; then
systemctl restart docker
fi
else
echo -e "\n${WARN} $(msg "warn.needManuallyDeleteConfig" "${File_DockerConfig}" "${BLUE}registry-mirrors${PLAIN}" "${BLUE}systemctl daemon-reload && systemctl restart docker${PLAIN}")\n"
fi
fi
return
fi
## 备份原有配置文件
if [ -d "${Dir_Docker}" ] && [ -e "${File_DockerConfig}" ]; then
if [ -e "${File_DockerConfigBackup}" ]; then
if [[ "${IGNORE_BACKUP_TIPS}" == "false" ]]; then
local ask_text="$(msg "interaction.backup.skipOverwrite")?"
if [[ "${CAN_USE_ADVANCED_INTERACTIVE_SELECTION}" == "true" ]]; then
echo ''
interactive_select_boolean "${BOLD}${ask_text}${PLAIN}"
if [[ "${_SELECT_RESULT}" == "false" ]]; then
echo ''
cp -rvf $File_DockerConfig $File_DockerConfigBackup 2>&1
fi
else
local CHOICE_BACKUP="$(echo -e "\n${BOLD}└─ ${ask_text} [Y/n] ${PLAIN}")"
read -rp "${CHOICE_BACKUP}" INPUT
[[ -z "${INPUT}" ]] && INPUT=Y
case $INPUT in
[Yy] | [Yy][Ee][Ss]) ;;
[Nn] | [Nn][Oo])
echo ''
cp -rvf $File_DockerConfig $File_DockerConfigBackup 2>&1
;;
*)
input_error "$(msg "error.defaultBehavior.noOverwrite")"
;;
esac
fi
fi
else
echo ''
cp -rvf $File_DockerConfig $File_DockerConfigBackup 2>&1
echo -e "\n$COMPLETE $(msg "info.backuped.dockerConfig")"
fi
sleep 2s
else
mkdir -p $Dir_Docker >/dev/null 2>&1
touch $File_DockerConfig
fi
echo -e '{\n "registry-mirrors": ["https://'"${SOURCE_REGISTRY}"'"]\n}' >$File_DockerConfig
## 重启服务
systemctl daemon-reload
if [[ "$(systemctl is-active docker 2>/dev/null)" == "active" ]]; then
systemctl restart docker
fi
}
## 仅修改 Docker Registry 镜像仓库源模式
function only_change_docker_registry_mirror() {
## 判定是否已安装
case "${SYSTEM_FACTIONS}" in
"${SYSTEM_DEBIAN}")
dpkg -l | grep docker-ce-cli -q
;;
"${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}" | "${SYSTEM_TENCENTOS}")
rpm -qa | grep docker-ce-cli -q
;;
esac
if [ $? -ne 0 ]; then
## 仅镜像仓库换源模式
if [[ "${ONLY_REGISTRY}" == "true" ]]; then
output_error "$(msg "result.registry.dockerEngineNotExsit" "${BLUE}--only-registry${PLAIN}")"
fi
fi
[ -d "${Dir_Docker}" ] || mkdir -p "${Dir_Docker}"
if [ -s "${File_DockerConfig}" ]; then
## 安装 jq
if ! command_exists jq; then
## 更新软件源
local package_manager
local commands=()
case "${SYSTEM_FACTIONS}" in
"${SYSTEM_DEBIAN}")
package_manager="apt-get"
commands+=("${package_manager} update")
;;
"${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}" | "${SYSTEM_TENCENTOS}")
package_manager="$(get_package_manager)"
commands+=("${package_manager} makecache")
;;
esac
if [[ "${PURE_MODE}" == "true" ]]; then
local exec_cmd=""
for cmd in "${commands[@]}"; do
if [[ -z "${exec_cmd}" ]]; then
exec_cmd="${cmd}"
else
exec_cmd="${exec_cmd} ; ${cmd}"
fi
done
echo ''
animate_exec "${exec_cmd}" "${SYNC_MIRROR_TEXT}"
else
echo ''
for cmd in "${commands[@]}"; do
eval "${cmd}"
done
fi
if [ $? -ne 0 ]; then
output_error "$(msg "error.sync" "${SYNC_MIRROR_TEXT}" "${BLUE}${package_manager}${PLAIN}")"
fi
$package_manager install -y jq
if ! command_exists jq; then
output_error "$(msg "error.installPackageFailed" "${BLUE}jq${PLAIN}")"
fi
fi
[ -s "${File_DockerConfig}" ] || echo "{}" >$File_DockerConfig
jq '.["registry-mirrors"] = ["https://'"${SOURCE_REGISTRY}"'"]' $File_DockerConfig >$File_DockerConfig.tmp && mv $File_DockerConfig.tmp $File_DockerConfig
else
echo -e '{\n "registry-mirrors": ["https://'"${SOURCE_REGISTRY}"'"]\n}' >$File_DockerConfig
fi
echo -e "\n${BLUE}\$${PLAIN} docker info --format '{{json .RegistryConfig.Mirrors}}'"
echo -e "\n${GREEN}${PLAIN} $(docker info --format '{{json .RegistryConfig.Mirrors}}')"
## 重启服务
systemctl daemon-reload
if [[ "$(systemctl is-active docker 2>/dev/null)" == "active" ]]; then
systemctl restart docker
fi
if [[ "${PURE_MODE}" != "true" ]]; then
echo -e "\n$COMPLETE $(msg "result.registry.success")"
fi
}
## 查看版本并验证安装结果
function check_installed_result() {
if command_exists docker; then
systemctl enable --now docker >/dev/null 2>&1
echo -en "\n$COMPLETE "
docker -v
if [ $? -eq 0 ]; then
echo -e " $(docker compose version 2>&1)"
# echo -e "\n$COMPLETE 安装完成"
else
echo -e "\n$FAIL $(msg "result.install.failed")"
local source_file package_manager
case "${SYSTEM_FACTIONS}" in
"${SYSTEM_DEBIAN}")
source_file="${File_DockerSourceList}"
package_manager="apt-get"
;;
"${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}" | "${SYSTEM_TENCENTOS}")
source_file="${File_DockerRepo}"
package_manager="$(get_package_manager)"
;;
esac
echo -e "\n$(msg "result.install.checkSourceFile" "cat ${source_file}")"
echo -e "$(msg "result.install.manuallyExecCmd" "${package_manager} install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin")\n"
exit 1
fi
if [[ "$(systemctl is-active docker 2>/dev/null)" != "active" ]]; then
sleep 2
systemctl disable --now docker >/dev/null 2>&1
sleep 2
systemctl enable --now docker >/dev/null 2>&1
sleep 2
if [[ "$(systemctl is-active docker)" != "active" ]]; then
echo -e "\n$WARN $(msg "result.install.notRunning")"
local start_cmd
if command_exists systemctl; then
start_cmd="systemctl start docker"
else
start_cmd="service docker start"
fi
echo -e "\n$TIP $(msg "result.install.manuallyRun" "${BLUE}${start_cmd}${PLAIN}")"
fi
fi
else
echo -e "\n$FAIL $(msg "result.install.failed")"
fi
}
## 选择系统包管理器
function get_package_manager() {
local command="yum"
case "${SYSTEM_JUDGMENT}" in
"${SYSTEM_RHEL}" | "${SYSTEM_CENTOS_STREAM}" | "${SYSTEM_ROCKY}" | "${SYSTEM_ALMALINUX}" | "${SYSTEM_ORACLE}")
case "${SYSTEM_VERSION_ID_MAJOR}" in
9 | 10)
command="dnf"
;;
esac
;;
"${SYSTEM_FEDORA}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}" | "${SYSTEM_TENCENTOS}")
command="dnf"
;;
esac
echo "${command}"
}
function interactive_select_list() {
_SELECT_RESULT=""
eval "local __values=(\"\${${1}[@]}\")"
local __labels=()
local message="${2}"
local selected=0
local start=0
local page_size=$(($(tput lines 2>/dev/null) - 3))
if [[ "${3}" ]]; then
eval "__labels=(\"\${${3}[@]}\")"
fi
function clear_menu() {
tput rc 2>/dev/null
for ((i = 0; i < ${#__values[@]} + 1; i++)); do
echo -e "\r\033[K"
done
tput rc 2>/dev/null
}
function cleanup() {
clear_menu
tput rc 2>/dev/null
tput cnorm 2>/dev/null
tput rmcup 2>/dev/null
echo -e "\n\033[1;44m $(msg "interaction.common.tip") \033[0m \033[31m$(msg "interaction.common.operationCanceled")\033[0m\n"
exit 130
}
function draw_menu() {
tput clear 2>/dev/null
tput cup 0 0 2>/dev/null
echo -e "${message}"
local end=$((start + page_size - 1))
local label
if [ $end -ge ${#__values[@]} ]; then
end=${#__values[@]}-1
fi
for ((i = start; i <= end; i++)); do
if [[ "${__labels[$i]}" ]]; then
label="${__labels[$i]}"
else
label="${__values[$i]}"
fi
if [ "$i" -eq "${selected}" ]; then
echo -e "\e[34;4m➤ ${label}\e[0m"
else
echo -e " ${label}"
fi
done
}
function read_key() {
IFS= read -rsn1 key
if [[ $key == $'\x1b' ]]; then
IFS= read -rsn2 key
key="$key"
fi
echo "$key"
}
tput smcup 2>/dev/null
tput sc 2>/dev/null
tput civis 2>/dev/null
trap "cleanup" INT TERM
draw_menu
while true; do
key=$(read_key)
case "$key" in
"[A" | "w" | "W")
if [ "${selected}" -gt 0 ]; then
selected=$((selected - 1))
if [ "${selected}" -lt "$start" ]; then
start=$((start - 1))
fi
fi
;;
"[B" | "s" | "S")
if [ "${selected}" -lt $((${#__values[@]} - 1)) ]; then
selected=$((selected + 1))
if [ "${selected}" -ge $((start + page_size)) ]; then
start=$((start + 1))
fi
fi
;;
"")
tput rmcup
break
;;
*) ;;
esac
draw_menu
done
tput cnorm 2>/dev/null
tput rmcup 2>/dev/null
_SELECT_RESULT="${__values[${selected}]}"
if [ "${__labels[${selected}]}" ]; then
_SELECT_RESULT="${_SELECT_RESULT}@@${__labels[${selected}]}"
fi
}
function interactive_select_boolean() {
_SELECT_RESULT=""
local selected=0
local message="$1"
local positive_title="${2:-"$(msg "interaction.common.yes")"}"
local negative_title="${3:-"$(msg "interaction.common.no")"}"
local menu_height=3
local original_line
function store_position() {
original_line=$(tput lines 2>/dev/null)
}
function clear_menu() {
for ((i = 0; i < $menu_height; i++)); do
tput cuu1 2>/dev/null
tput el 2>/dev/null
done
}
function cleanup() {
clear_menu
tput cnorm 2>/dev/null
echo -e "\n\033[1;44m $(msg "interaction.common.tip") \033[0m \033[31m$(msg "interaction.common.operationCanceled")\033[0m\n"
exit 130
}
function draw_menu() {
echo -e "╭─ ${message}"
echo -e ""
if [ "${selected}" -eq 0 ]; then
echo -e "╰─ \033[34m●\033[0m ${positive_title}\033[2m / ○ ${negative_title}\033[0m"
else
echo -e "╰─ \033[2m○ ${positive_title} / \033[0m\033[34m●\033[0m ${negative_title}"
fi
}
function read_key() {
IFS= read -rsn1 key
if [[ $key == $'\x1b' ]]; then
IFS= read -rsn2 key
key="$key"
fi
echo "$key"
}
tput civis 2>/dev/null
store_position
trap "cleanup" INT TERM
draw_menu
while true; do
key=$(read_key)
case "$key" in
"[D" | "a" | "A")
if [ "${selected}" -gt 0 ]; then
selected=$((selected - 1))
clear_menu
draw_menu
fi
;;
"[C" | "d" | "D")
if [ "${selected}" -lt 1 ]; then
selected=$((selected + 1))
clear_menu
draw_menu
fi
;;
"")
clear_menu
break
;;
*) ;;
esac
done
echo -e "╭─ ${message}"
echo -e ""
if [ "${selected}" -eq 0 ]; then
echo -e "╰─ \033[32m●\033[0m \033[1m${positive_title}\033[0m\033[2m / ○ ${negative_title}\033[0m"
_SELECT_RESULT="true"
else
echo -e "╰─ \033[2m○ ${positive_title} / \033[0m\033[32m●\033[0m \033[1m${negative_title}\033[0m"
_SELECT_RESULT="false"
fi
tput cnorm 2>/dev/null
}
function animate_exec() {
local cmd="$1"
local title="$2"
local max_lines=${3:-5}
local spinner_style="${4:-dots}"
local refresh_rate="${5:-0.1}"
local -A spinners=([dots]="⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏" [circle]="◐ ◓ ◑ ◒" [classic]="-\\ |/")
local -A recommended_rates=([dots]="0.08" [circle]="0.12" [classic]="0.12")
[[ -z "${spinners[$spinner_style]}" ]] && spinner_style="dots"
[[ "${refresh_rate}" == "0.1" ]] && refresh_rate="${recommended_rates[$spinner_style]}"
local term_width=$(tput cols 2>/dev/null || echo 80)
local display_width=$((term_width - 2))
function simple_truncate() {
local line="$1"
local truncate_marker="..."
local max_length=$((display_width - 3))
if [[ "${line}" =~ ^[[:ascii:]]*$ && ${#line} -le $display_width ]]; then
echo "${line}"
return
fi
local non_ascii_count=$(echo "${line}" | sed "s|[0-9a-zA-Z -~]||g; s| ||g" | wc -m)
local total_length=${#line}
local display_length=$((total_length + non_ascii_count))
local quote_count=0
[[ $(echo "${line}" | grep -c "“") -gt 0 ]] && ((quote_count += "$(echo "${line}" | grep -c "“")"))
[[ $(echo "${line}" | grep -c "”") -gt 0 ]] && ((quote_count += "$(echo "${line}" | grep -c "”")"))
[[ $(echo "${line}" | grep -c "") -gt 0 ]] && ((quote_count += "$(echo "${line}" | grep -c "")"))
[[ $(echo "${line}" | grep -c "") -gt 0 ]] && ((quote_count += "$(echo "${line}" | grep -c "")"))
display_length=$((display_length - quote_count))
if [[ $display_length -le $display_width ]]; then
echo "$line"
return
fi
local result=""
local current_width=0
local i=0
while [ $i -lt ${#line} ]; do
local char="${line:$i:1}"
local char_width=1
if ! [[ "$char" =~ [0-9a-zA-Z\.\=\:\_\(\)\'\"\-\/\!\·] ]]; then
if [[ "$char" != "" && "$char" != "" && "$char" != "" && "$char" != "" ]]; then
char_width=2
fi
fi
if [[ $((current_width + char_width)) -gt $max_length ]]; then
echo "${result}${truncate_marker}"
return
fi
result+="${char}"
current_width=$((current_width + char_width))
((i++))
done
echo "${line}"
}
function cleanup() {
[ -f "${temp_file}" ] && rm -f "${temp_file}"
tput cnorm 2>/dev/null
echo -e "\n\033[1;44m $(msg "interaction.common.tip") \033[0m \033[31m$(msg "interaction.common.operationCanceled")\033[0m\n"
exit 130
}
function make_temp_file() {
local temp_dirs=("." "/tmp")
local tmp_file=""
for dir in "${temp_dirs[@]}"; do
[[ ! -d "${dir}" || ! -w "${dir}" ]] && continue
tmp_file="${dir}/animate_exec_$$_$(date +%s)"
touch "${tmp_file}" 2>/dev/null || continue
if [[ -f "${tmp_file}" && -w "${tmp_file}" ]]; then
echo "${tmp_file}"
return
fi
done
echo "${tmp_file}"
}
function update_display() {
local current_size=$(wc -c <"${temp_file}" 2>/dev/null || echo 0)
if [[ $current_size -le $last_size ]]; then
return 1
fi
local -a lines=()
mapfile -t -n "${max_lines}" lines < <(tail -n "$max_lines" "${temp_file}")
local -a processed_lines=()
for ((i = 0; i < ${#lines[@]}; i++)); do
processed_lines[i]=$(simple_truncate "${lines[i]}")
done
tput cud1 2>/dev/null
echo -ne "\r\033[K"
tput cud1 2>/dev/null
for ((i = 0; i < $max_lines; i++)); do
echo -ne "\r\033[K"
[[ $i -lt ${#processed_lines[@]} ]] && echo -ne "\033[2m${processed_lines[$i]}\033[0m"
[[ $i -lt $((max_lines - 1)) ]] && tput cud1 2>/dev/null
done
for ((i = 0; i < $max_lines + 1; i++)); do
tput cuu1 2>/dev/null
done
last_size=$current_size
return 0
}
local spinner_frames=(${spinners[$spinner_style]})
local temp_file="$(make_temp_file)"
trap "cleanup" INT TERM
tput civis 2>/dev/null
echo ''
echo ''
for ((i = 0; i < $max_lines; i++)); do
echo ''
done
eval "${cmd}" >"${temp_file}" 2>&1 &
local cmd_pid=$!
local last_size=0
local spin_idx=0
tput cuu $((max_lines + 2)) 2>/dev/null
sleep 0.05
echo -ne "\r\033[K◉ ${title} [\033[1m\033[34m${spinner_frames[$spin_idx]}\033[0m]"
spin_idx=$(((spin_idx + 1) % ${#spinner_frames[@]}))
update_display
local update_count=0
local adaptive_rate=$refresh_rate
while kill -0 $cmd_pid 2>/dev/null; do
echo -ne "\r\033[K◉ ${title} [\033[1m\033[34m${spinner_frames[$spin_idx]}\033[0m]"
spin_idx=$(((spin_idx + 1) % ${#spinner_frames[@]}))
if update_display; then
update_count=$((update_count + 1))
if [[ $update_count -gt 5 ]]; then
adaptive_rate=$(awk "BEGIN {print $adaptive_rate * 1.5; exit}")
[[ $(awk "BEGIN {print ($adaptive_rate > 0.5); exit}") -eq 1 ]] && adaptive_rate=0.5
update_count=0
fi
else
update_count=0
adaptive_rate=$refresh_rate
fi
sleep $adaptive_rate
done
wait $cmd_pid
local exit_status=$?
update_display
if [ $exit_status -eq 0 ]; then
echo -ne "\r\033[K◉ ${title} [\033[1m\033[32m✓\033[0m]\n"
else
echo -ne "\r\033[K◉ ${title} [\033[1m\033[31m✗\033[0m]\n"
fi
echo -ne "\r\033[K\n"
local actual_lines=$(wc -l <"${temp_file}" 2>/dev/null || echo 0)
[[ $actual_lines -gt $max_lines ]] && actual_lines=$max_lines
if [[ $actual_lines -gt 0 ]]; then
local -a final_lines=()
mapfile -t -n "$actual_lines" final_lines < <(tail -n "$actual_lines" "${temp_file}")
for ((i = 0; i < actual_lines; i++)); do
local line=$(simple_truncate "${final_lines[$i]}")
echo -ne "\r\033[K\033[2m${line}\033[0m\n"
done
fi
tput cnorm 2>/dev/null
rm -f "${temp_file}"
return $exit_status
}
##############################################################################
MESSAGE_LANG_DEFAULT='zh-hans'
MESSAGE_LANG_KEYS=(
"zh-hans"
"zh-hant"
"en"
)
declare -A MESSAGE_LANG_DISPLAY=(
['zh-hans']='简体中文'
['zh-hant']='繁體中文'
['en']='English'
)
declare -A MESSAGE_CONTENTS
function msg() {
local key="$1"
shift
local text="${MESSAGE_CONTENTS[${key}]}"
if [[ -z "${text}" ]]; then
echo "${key}"
return
fi
while [[ $# -gt 0 ]]; do
if [[ "${text}" == *"{}"* ]]; then
text="${text/\{\}/$1}"
else
break
fi
shift
done
echo "${text}"
}
function init_msg_pack() {
function load_pack() {
local func_name="${1}"
if declare -f "${func_name}" >/dev/null 2>&1; then
eval "${func_name}"
fi
}
local current_lang="${1:-"${MESSAGE_LANG_DEFAULT}"}"
current_lang="$(echo "${current_lang}" | sed 's/^-*//')"
current_lang="${current_lang,,}"
if [[ "${MESSAGE_LANG_DISPLAY[${current_lang}]}" ]]; then
current_lang="${current_lang//-/_}"
load_pack "msg_pack_${current_lang}"
fi
}
function msg_pack_zh_hans() {
MESSAGE_CONTENTS=(
['start.welcome']='欢迎使用 Docker Engine 安装与换源脚本'
['start.runtimeEnv']='运行环境'
['start.dateTime']='系统时间'
['end.moreInfo']='脚本运行完毕,更多使用教程详见官网'
['end.sponsorAds']='【赞助商广告】'
['error.cmd.options.needConfirm']='请确认后重新输入'
['error.cmd.options.needSpecify']='请在该选项后指定{}'
['error.cmd.options.invalid']='命令选项 {} 无效,{}'
['error.cmd.options.validAddress']='有效的地址'
['error.cmd.options.sourceAddress']='软件源地址'
['error.cmd.options.registryAddress']='镜像仓库地址'
['error.cmd.options.sourceRepository']='软件源仓库'
['error.cmd.options.validVersion']='有效的版本号'
['error.cmd.options.ceRepositoryVersion']='Docker CE 软件源仓库的版本号'
['error.cmd.options.version']='版本号'
['error.cmd.options.codename']='版本代号'
['error.cmd.options.boolean']=' true 或 false '
['error.cmd.options.protocol']=' http 或 https '
['error.cmd.options.needProtocol']=' Web 协议(http/https)'
['error.cmd.options.validLangKey']='有效的语言 ID '
['error.cmd.options.langKey']='语言 '
['error.unsupportSystem1']='不支持当前操作系统({}'
['error.unsupportSystem2']='不支持当前操作系统({}),请参考如下命令自行安装:'
['error.unknownSystem']='未知系统'
['error.unsupportX86_32']='Docker Engine 不支持安装在 x86_32 架构的环境上!'
['error.unknownArch']='未知的系统架构:{}'
['error.unsupportS390x']='请查阅 RHEL 发行版声明以了解 s390x 支持'
['error.input']='输入错误,{}'
['error.needRoot']='权限不足,请使用 Root 用户运行本脚本'
['error.sync']='{}出错,请先解决系统原有软件源错误以确保 {} 软件包管理工具可用!'
['error.downloadGPG']='GPG 密钥下载失败,请检查网络或更换 Docker CE 软件源后重试!'
['error.queryVersionFailed']='查询 Docker Engine 版本列表失败!'
['error.designatedVersion']='指定的 Docker Engine 版本不存在或不支持安装!'
['error.invalidVersion']='请输入正确的版本号!'
['error.reEnter']='输入错误请重新输入!'
['error.installDockerEngineFailed']='安装 Docker Engine 失败!'
['error.installPackageFailed']='软件包 {} 安装失败,请自行安装后重新运行脚本!'
['error.defaultBehavior.https']='默认使用 HTTPS 协议'
['error.defaultBehavior.noClose']='默认不关闭'
['error.defaultBehavior.installLatest']='默认安装最新版本'
['error.defaultBehavior.noOverwrite']='默认不覆盖'
['error.defaultBehavior.noUseIntranetSource']='默认不使用内网地址'
['warn.usedIntranetSource']='已切换至内网专用地址,仅限在特定环境下使用!'
['warn.needValidNumberIndex']='请输入有效的数字序号!'
['warn.needInputNumberIndex']='请输入数字序号!'
['warn.needManuallyDeleteConfig']='请自行删除 {} 中的 {} 配置并重启服务 {}'
['tip.skipInstallDockerEngine']='检测到系统已安装 Docker Engine 且是最新版本,跳过安装'
['info.backuped.dockerConfig']='已备份原有 Docker 配置文件'
['interaction.source.type.public']='公网'
['interaction.source.type.intranet']='内网'
['interaction.source.type.select']='请选择 Docker CE 源的网络地址(访问方式)'
['interaction.source.type.usePublicAddress']='默认使用 Docker CE 源的公网地址,是否继续'
['interaction.source.dockerCE.select']='请选择你想使用的 Docker CE 源:'
['interaction.source.dockerCE.selectAndInput']='请选择并输入你想使用的 Docker CE 源'
['interaction.source.dockerRegistry.select']='请选择你想使用的 Docker Registry 源:'
['interaction.source.dockerRegistry.selectAndInput']='请选择并输入你想使用的 Docker Registry 源'
['interaction.protocol.select']='请选择 Docker CE 软件源的网络协议:'
['interaction.protocol.useHttp']='Docker CE 软件源是否使用 HTTP 协议'
['interaction.firewall.close']='是否关闭系统防火墙和 SELinux '
['interaction.install.selectVersion']='请选择你想安装的版本:'
['interaction.install.selectedVersion']='指定安装版本:'
['interaction.install.selectedTitle']='请选择你要安装的版本,如:{}'
['interaction.install.inputVersion']='请根据上面的列表,选择并输入你想要安装的具体版本号:'
['interaction.install.latestVersion']='Docker Engine 是否安装最新版本'
['interaction.backup.skipOverwrite']='检测到已备份的 Docker 配置文件,是否跳过覆盖备份'
['interaction.common.tip']='提示'
['interaction.common.operationCanceled']='操作已取消'
['interaction.common.yes']='是'
['interaction.common.no']='否'
['work.installDependents']='安装环境软件包'
['work.installDockerEngine']='安装 Docker Engine'
['source.sync.text1']='更新软件源'
['source.sync.text2']='生成软件源缓存'
['result.install.failed']='安装失败'
['result.install.checkSourceFile']='检查源文件:'
['result.install.manuallyExecCmd']='请尝试手动执行安装命令:{}'
['result.install.notRunning']='检测到 Docker 服务启动异常,可尝试再次执行本脚本重试'
['result.install.manuallyRun']='请执行 {} 命令尝试启动或自行查询错误原因'
['result.registry.success']='已更换镜像仓库'
['result.registry.dockerEngineNotExsit']='当前尚未安装 Docker Engine请取消设置 {} 命令选项后重新执行脚本!'
['commands.help']='命令选项(名称/含义/值)
--source 指定 Docker CE 软件源地址(域名或IP) 地址
--source-registry 指定 Docker 镜像仓库地址(域名或IP) 地址
--branch 指定 Docker CE 软件源仓库(路径) 仓库名
--branch-version 指定 Docker CE 软件源仓库版本 版本号
--designated-version 指定 Docker Engine 安装版本 版本号
--codename 指定 Debian 系操作系统的版本代号 代号名称
--protocol 指定 Docker CE 软件源的 Web 协议 http 或 https
--use-intranet-source 是否优先使用内网 Docker CE 软件源地址 true 或 false
--install-latest 是否安装最新版本的 Docker Engine true 或 false
--close-firewall 是否关闭防火墙 true 或 false
--clean-screen 是否在运行前清除屏幕上的所有内容 true 或 false
--lang 指定脚本使用的语言 ID 语言
--only-registry 仅更换镜像仓库模式 无
--ignore-backup-tips 忽略覆盖备份提示 无
--pure-mode 纯净模式,精简打印内容 无
--help 查看帮助菜单 无
问题报告 {}'
['mirrors.dockerCE.0']='阿里云'
['mirrors.dockerCE.1']='腾讯云'
['mirrors.dockerCE.2']='华为云'
['mirrors.dockerCE.3']='移动云'
['mirrors.dockerCE.4']='网易'
['mirrors.dockerCE.5']='火山引擎'
['mirrors.dockerCE.6']='微软 Azure 中国'
['mirrors.dockerCE.7']='清华大学'
['mirrors.dockerCE.8']='北京大学'
['mirrors.dockerCE.9']='浙江大学'
['mirrors.dockerCE.10']='南京大学'
['mirrors.dockerCE.11']='上海交通大学'
['mirrors.dockerCE.12']='重庆邮电大学'
['mirrors.dockerCE.13']='中国科学技术大学'
['mirrors.dockerCE.14']='中国科学院软件研究所'
['mirrors.dockerCE.15']='官方源'
['mirrors.registry.0']='毫秒镜像(推荐)'
['mirrors.registry.1']='Docker Proxy'
['mirrors.registry.2']='DaoCloud 道客'
['mirrors.registry.3']='1Panel 镜像'
['mirrors.registry.4']='阿里云(杭州)'
['mirrors.registry.5']='阿里云(上海)'
['mirrors.registry.6']='阿里云(青岛)'
['mirrors.registry.7']='阿里云(北京)'
['mirrors.registry.8']='阿里云(张家口)'
['mirrors.registry.9']='阿里云(呼和浩特)'
['mirrors.registry.10']='阿里云(乌兰察布)'
['mirrors.registry.11']='阿里云(深圳)'
['mirrors.registry.12']='阿里云(河源)'
['mirrors.registry.13']='阿里云(广州)'
['mirrors.registry.14']='阿里云(成都)'
['mirrors.registry.15']='阿里云(香港)'
['mirrors.registry.16']='阿里云(日本-东京)'
['mirrors.registry.17']='阿里云(新加坡)'
['mirrors.registry.18']='阿里云(马来西亚-吉隆坡)'
['mirrors.registry.19']='阿里云(印度尼西亚-雅加达)'
['mirrors.registry.20']='阿里云(德国-法兰克福)'
['mirrors.registry.21']='阿里云(英国-伦敦)'
['mirrors.registry.22']='阿里云(美国西部-硅谷)'
['mirrors.registry.23']='阿里云(美国东部-弗吉尼亚)'
['mirrors.registry.24']='阿里云(阿联酋-迪拜)'
['mirrors.registry.25']='腾讯云'
['mirrors.registry.26']='谷歌云(北美)'
['mirrors.registry.27']='谷歌云(亚洲)'
['mirrors.registry.28']='谷歌云(欧洲)'
['mirrors.registry.29']='官方 Docker Hub'
)
}
function msg_pack_zh_hant() {
MESSAGE_CONTENTS=(
['start.welcome']='歡迎使用 Docker Engine 安裝與換源腳本'
['start.runtimeEnv']='執行環境'
['start.dateTime']='系統時間'
['end.moreInfo']='腳本執行完畢,更多使用教學詳見官網'
['end.sponsorAds']='【贊助商廣告】'
['error.cmd.options.needConfirm']='請確認後重新輸入'
['error.cmd.options.needSpecify']='請在該選項後指定{}'
['error.cmd.options.invalid']='命令選項 {} 無效,{}'
['error.cmd.options.validAddress']='有效的位址'
['error.cmd.options.sourceAddress']='軟體源位址'
['error.cmd.options.registryAddress']='映象倉庫位址'
['error.cmd.options.sourceRepository']='軟體源倉庫'
['error.cmd.options.validVersion']='有效的版本號'
['error.cmd.options.ceRepositoryVersion']='Docker CE 軟體源倉庫的版本號'
['error.cmd.options.version']='版本號'
['error.cmd.options.codename']='版本代號'
['error.cmd.options.boolean']=' true 或 false '
['error.cmd.options.protocol']=' http 或 https '
['error.cmd.options.needProtocol']=' Web 協定(http/https)'
['error.cmd.options.validLangKey']='有效的語言 ID '
['error.cmd.options.langKey']='語言 ID '
['error.unsupportSystem1']='不支援當前作業系統({}'
['error.unsupportSystem2']='不支援當前作業系統({}),請參考如下命令自行安裝:\n\n{}'
['error.unknownSystem']='未知系統'
['error.unsupportX86_32']='Docker Engine 不支援安裝在 x86_32 架構的環境上!'
['error.unknownArch']='未知的系統架構:{}'
['error.unsupportS390x']='請查閱 RHEL 發行版宣告以瞭解 s390x 支援'
['error.input']='輸入錯誤,{}'
['error.needRoot']='權限不足,請使用 Root 使用者執行本腳本'
['error.sync']='{}出錯,請先解決系統原有軟體源錯誤以確保 {} 軟體包管理工具可用!'
['error.downloadGPG']='GPG 金鑰下載失敗,請檢查網路或更換 Docker CE 軟體源後重試!'
['error.queryVersionFailed']='查詢 Docker Engine 版本清單失敗!'
['error.designatedVersion']='指定的 Docker Engine 版本不存在或不支援安裝!'
['error.invalidVersion']='請輸入正確的版本號!'
['error.reEnter']='輸入錯誤請重新輸入!'
['error.installDockerEngineFailed']='安裝 Docker Engine 失敗!'
['error.installPackageFailed']='軟體包 {} 安裝失敗,請自行安裝後重新執行腳本!'
['error.defaultBehavior.https']='預設使用 HTTPS 協定'
['error.defaultBehavior.noClose']='預設不關閉'
['error.defaultBehavior.installLatest']='預設安裝最新版本'
['error.defaultBehavior.noOverwrite']='預設不覆寫'
['error.defaultBehavior.noUseIntranetSource']='預設不使用內網位址'
['warn.usedIntranetSource']='已切換至內網專用位址,僅限在特定環境下使用!'
['warn.needValidNumberIndex']='請輸入有效的數字序號!'
['warn.needInputNumberIndex']='請輸入數字序號!'
['warn.needManuallyDeleteConfig']='請自行刪除 {} 中的 {} 設定並重新啟動服務 {}'
['tip.skipInstallDockerEngine']='偵測到系統已安裝 Docker Engine 且是最新版本,跳過安裝'
['info.backuped.dockerConfig']='已備份原有 Docker 設定檔'
['interaction.source.type.public']='公網'
['interaction.source.type.intranet']='內網'
['interaction.source.type.select']='請選擇 Docker CE 源的網路位址(存取方式)'
['interaction.source.type.usePublicAddress']='預設使用 Docker CE 源的公網位址,是否繼續'
['interaction.source.dockerCE.select']='請選擇你想使用的 Docker CE 源:'
['interaction.source.dockerCE.selectAndInput']='請選擇並輸入你想使用的 Docker CE 源'
['interaction.source.dockerRegistry.select']='請選擇你想使用的 Docker Registry 源:'
['interaction.source.dockerRegistry.selectAndInput']='請選擇並輸入你想使用的 Docker Registry 源'
['interaction.protocol.select']='請選擇 Docker CE 軟體源的網路協定:'
['interaction.protocol.useHttp']='Docker CE 軟體源是否使用 HTTP 協定'
['interaction.firewall.close']='是否關閉系統防火牆和 SELinux '
['interaction.install.selectVersion']='請選擇你想安裝的版本:'
['interaction.install.selectedVersion']='指定安裝版本:'
['interaction.install.selectedTitle']='請選擇你要安裝的版本,如:{}'
['interaction.install.inputVersion']='請根據上面的清單,選擇並輸入你想要安裝的具體版本號:'
['interaction.install.latestVersion']='Docker Engine 是否安裝最新版本'
['interaction.backup.skipOverwrite']='偵測到已備份的 Docker 設定檔,是否跳過覆寫備份'
['interaction.common.tip']='提示'
['interaction.common.operationCanceled']='操作已取消'
['interaction.common.yes']='是'
['interaction.common.no']='否'
['work.installDependents']='安裝環境軟體包'
['work.installDockerEngine']='安裝 Docker Engine'
['source.sync.text1']='更新軟體源'
['source.sync.text2']='產生軟體源快取'
['result.install.failed']='安裝失敗'
['result.install.checkSourceFile']='檢查源檔案:'
['result.install.manuallyExecCmd']='請嘗試手動執行安裝命令:{}'
['result.install.notRunning']='偵測到 Docker 服務啟動異常,可嘗試再次執行本腳本重試'
['result.install.manuallyRun']='請執行 {} 命令嘗試啟動或自行查詢錯誤原因'
['result.registry.success']='已更換映象倉庫'
['result.registry.dockerEngineNotExsit']='目前尚未安裝 Docker Engine請取消設定 {} 命令選項後重新執行腳本!'
['commands.help']='命令選項(名稱/含義/值)
--source 指定 Docker CE 軟體源位址(網域名稱或IP) 位址
--source-registry 指定 Docker 映像倉庫位址 (網域名稱或IP) 位址
--branch 指定 Docker CE 軟體源倉庫 (路徑) 倉庫名稱
--branch-version 指定 Docker CE 軟體源倉庫版本 版本號
--designated-version 指定 Docker Engine 安裝版本 版本號
--codename 指定 Debian 係作業系統的版本代號 代號名稱
--protocol 指定 Docker CE 源的 Web 協定 http 或 https
--use-intranet-source 是否優先使用內部網路 Docker CE 軟體源位址 true 或 false
--install-latest 是否安裝最新版本的 Docker Engine true 或 false
--close-firewall 是否關閉防火牆 true 或 false
--clean-screen 是否在運行前清除螢幕上的所有內容 true 或 false
--lang 指定腳本輸出的語言 语言
--only-registry 僅更換映像倉庫模式 無
--ignore-backup-tips 忽略覆蓋備份提示 無
--pure-mode 純淨模式,精簡列印內容 無
--help 查看幫助選單 無
問題報告 {}'
['mirrors.dockerCE.0']='阿里雲'
['mirrors.dockerCE.1']='騰訊雲'
['mirrors.dockerCE.2']='華為雲'
['mirrors.dockerCE.3']='移動雲'
['mirrors.dockerCE.4']='網易'
['mirrors.dockerCE.5']='火山引擎'
['mirrors.dockerCE.6']='微軟 Azure 中國'
['mirrors.dockerCE.7']='清華大學'
['mirrors.dockerCE.8']='北京大學'
['mirrors.dockerCE.9']='浙江大學'
['mirrors.dockerCE.10']='南京大學'
['mirrors.dockerCE.11']='上海交通大學'
['mirrors.dockerCE.12']='重慶郵電大學'
['mirrors.dockerCE.13']='中國科學技術大學'
['mirrors.dockerCE.14']='中國科學院軟體研究所'
['mirrors.dockerCE.15']='官方源'
['mirrors.registry.0']='毫秒鏡像(推薦)'
['mirrors.registry.1']='Docker Proxy'
['mirrors.registry.2']='DaoCloud 道客'
['mirrors.registry.3']='1Panel 鏡像'
['mirrors.registry.4']='阿里雲(杭州)'
['mirrors.registry.5']='阿里雲(上海)'
['mirrors.registry.6']='阿里雲(青島)'
['mirrors.registry.7']='阿里雲(北京)'
['mirrors.registry.8']='阿里雲(張家口)'
['mirrors.registry.9']='阿里雲(呼和浩特)'
['mirrors.registry.10']='阿里雲(烏蘭察布)'
['mirrors.registry.11']='阿里雲(深圳)'
['mirrors.registry.12']='阿里雲(河源)'
['mirrors.registry.13']='阿里雲(廣州)'
['mirrors.registry.14']='阿里雲(成都)'
['mirrors.registry.15']='阿里雲(香港)'
['mirrors.registry.16']='阿里雲(日本-東京)'
['mirrors.registry.17']='阿里雲(新加坡)'
['mirrors.registry.18']='阿里雲(馬來西亞-吉隆坡)'
['mirrors.registry.19']='阿里雲(印度尼西亞-雅加達)'
['mirrors.registry.20']='阿里雲(德國-法蘭克福)'
['mirrors.registry.21']='阿里雲(英國-倫敦)'
['mirrors.registry.22']='阿里雲(美國西部-矽谷)'
['mirrors.registry.23']='阿里雲(美國東部-弗吉尼亞)'
['mirrors.registry.24']='阿里雲(阿聯酋-迪拜)'
['mirrors.registry.25']='騰訊雲'
['mirrors.registry.26']='谷歌雲(北美)'
['mirrors.registry.27']='谷歌雲(亞洲)'
['mirrors.registry.28']='谷歌雲(歐洲)'
['mirrors.registry.29']='官方 Docker Hub'
)
SPONSOR_ADS[0]="1Panel · 新一代的 Linux 伺服器維運管理面板 ➜ \033[3mhttps://1panel.cn\033[0m"
}
function msg_pack_en() {
MESSAGE_CONTENTS=(
['start.welcome']='Docker installation & mirror switcher'
['start.runtimeEnv']='Runtime Env'
['start.dateTime']='System Time'
['end.moreInfo']='Script execution completed, visit our website for more tutorials'
['end.sponsorAds']='[Sponsor Ads]'
['error.cmd.options.needConfirm']='Please confirm and re-enter'
['error.cmd.options.needSpecify']='Please specify {} after this option'
['error.cmd.options.invalid']='Command option {} is invalid, {}!'
['error.cmd.options.validAddress']='a valid address'
['error.cmd.options.sourceAddress']='mirror address'
['error.cmd.options.registryAddress']='registry mirror address'
['error.cmd.options.sourceRepository']='mirror repository'
['error.cmd.options.validVersion']='a valid version number'
['error.cmd.options.ceRepositoryVersion']='Docker CE mirror repository version'
['error.cmd.options.version']='version number'
['error.cmd.options.codename']='version codename'
['error.cmd.options.boolean']=' true or false '
['error.cmd.options.protocol']=' http or https '
['error.cmd.options.needProtocol']=' Web protocol(http/https)'
['error.cmd.options.validLangKey']='A valid language ID '
['error.cmd.options.langKey']='language ID '
['error.unsupportSystem1']='Unsupported operating system ({})'
['error.unsupportSystem2']='Unsupported operating system ({}), please install manually with commands:'
['error.unknownSystem']='Unknown system'
['error.unsupportX86_32']='Docker Engine does not support installation on x86_32 architecture!'
['error.unknownArch']='Unknown system architecture: {}'
['error.unsupportS390x']='Please refer to RHEL distribution announcement for s390x support'
['error.input']='Input error, {}!'
['error.needRoot']='Insufficient permissions, please run this script as Root user'
['error.sync']='{} failed. Please fix system software sources (package repositories) so the {} package manager is available!'
['error.downloadGPG']='GPG key download failed, please check network or switch Docker CE mirror and retry!'
['error.queryVersionFailed']='Failed to query Docker Engine version list!'
['error.designatedVersion']='Specified Docker Engine version does not exist or is not supported for installation!'
['error.invalidVersion']='Please enter a valid version number!'
['error.reEnter']='Input error, please re-enter!'
['error.installDockerEngineFailed']='Docker Engine installation failed!'
['error.installPackageFailed']='Package {} installation failed, please install manually and rerun script!'
['error.defaultBehavior.https']='Using HTTPS protocol by default'
['error.defaultBehavior.noClose']='Not closing by default'
['error.defaultBehavior.installLatest']='Installing latest version by default'
['error.defaultBehavior.noOverwrite']='Not overwriting by default'
['error.defaultBehavior.noUseIntranetSource']='Not using intranet address by default'
['warn.usedIntranetSource']='Switched to intranet-only address, use only in specific environments!'
['warn.needValidNumberIndex']='Please enter a valid number index!'
['warn.needInputNumberIndex']='Please enter a number index!'
['warn.needManuallyDeleteConfig']='Please manually delete {} configuration in {} and restart service {}'
['tip.skipInstallDockerEngine']='Detected Docker Engine is already installed with latest version, skipping installation'
['info.backuped.dockerConfig']='Original Docker config file has been backed up'
['interaction.source.type.public']='Public'
['interaction.source.type.intranet']='Intranet'
['interaction.source.type.select']='Please select network address (access method) for Docker CE mirror:'
['interaction.source.type.usePublicAddress']='Use public network address for Docker CE mirror by default, continue'
['interaction.source.dockerCE.select']='Please select the Docker CE mirror you want to use:'
['interaction.source.dockerCE.selectAndInput']='Please select and enter the Docker CE mirror you want to use'
['interaction.source.dockerRegistry.select']='Please select the Docker Registry mirror you want to use:'
['interaction.source.dockerRegistry.selectAndInput']='Please select and enter the Docker Registry mirror you want to use'
['interaction.protocol.select']='Please select network protocol for Docker CE mirror:'
['interaction.protocol.useHttp']='Use HTTP protocol for Docker CE mirror'
['interaction.firewall.close']='Close system firewall and SELinux'
['interaction.install.selectVersion']='Please select the version you want to install:'
['interaction.install.selectedVersion']='Specified installation version:'
['interaction.install.selectedTitle']='Please select the version to install, e.g.: {}'
['interaction.install.inputVersion']='Based on the list above, please select and enter the specific version you want to install:'
['interaction.install.latestVersion']='Install latest version of Docker Engine'
['interaction.backup.skipOverwrite']='Detected existing backup of Docker config file, skip overwriting backup'
['interaction.common.tip']='Tip'
['interaction.common.operationCanceled']='Operation canceled'
['interaction.common.yes']='Yes'
['interaction.common.no']='No'
['work.installDependents']='Install environment packages'
['work.installDockerEngine']='Install Docker Engine'
['source.sync.text1']='Update APT package index'
['source.sync.text2']='Generate mirror cache'
['result.install.failed']='Installation failed'
['result.install.checkSourceFile']='Check source file:'
['result.install.manuallyExecCmd']='Please try manually executing installation command: {}'
['result.install.notRunning']='Detected Docker service startup error, try running this script again'
['result.install.manuallyRun']='Please execute {} command to try starting or investigate error cause'
['result.registry.success']='Registry mirror replaced successfully'
['result.registry.dockerEngineNotExsit']='Docker Engine is not installed yet, please remove {} command option and rerun script!'
['commands.help']='Command options(name/meaning/value):
--source Specify Docker CE mirror address (domain or IP) address
--source-registry Specify Docker Registry mirror address (domain or IP) address
--branch Specify Docker CE mirror repository (path) repo name
--branch-version Specify Docker CE mirror repository version version
--designated-version Specify Docker Engine installation version version
--codename Specify Debian-based OS codename codename
--protocol Specify Web protocol for Docker CE mirror http or https
--use-intranet-source Prefer intranet Docker CE mirror address true or false
--install-latest Whether to install the latest Docker Engine true or false
--close-firewall Whether to disable the firewall true or false
--clean-screen Whether to clear the screen before running true or false
--lang Specify the language of the script output language
--only-registry Only switch registry mirror mode none
--ignore-backup-tips Ignore backup overwrite prompt (do not backup) none
--pure-mode Pure mode, minimal output none
--help Show help menu none
Issue Report {}'
['mirrors.dockerCE.0']='Alibaba Cloud'
['mirrors.dockerCE.1']='Tencent Cloud'
['mirrors.dockerCE.2']='Huawei Cloud'
['mirrors.dockerCE.3']='China Mobile Cloud'
['mirrors.dockerCE.4']='NetEase'
['mirrors.dockerCE.5']='Volcengine'
['mirrors.dockerCE.6']='Microsoft Azure China'
['mirrors.dockerCE.7']='Tsinghua University'
['mirrors.dockerCE.8']='Peking University'
['mirrors.dockerCE.9']='Zhejiang University'
['mirrors.dockerCE.10']='Nanjing University'
['mirrors.dockerCE.11']='Shanghai Jiao Tong University'
['mirrors.dockerCE.12']='Chongqing University of Posts and Telecommunications'
['mirrors.dockerCE.13']='University of Science and Technology of China'
['mirrors.dockerCE.14']='Institute of Software, Chinese Academy of Sciences'
['mirrors.dockerCE.15']='Official Source'
['mirrors.registry.0']='Millisecond Mirror (recommended)'
['mirrors.registry.1']='Docker Proxy'
['mirrors.registry.2']='DaoCloud'
['mirrors.registry.3']='1Panel Mirror'
['mirrors.registry.4']='Alibaba Cloud (Hangzhou)'
['mirrors.registry.5']='Alibaba Cloud (Shanghai)'
['mirrors.registry.6']='Alibaba Cloud (Qingdao)'
['mirrors.registry.7']='Alibaba Cloud (Beijing)'
['mirrors.registry.8']='Alibaba Cloud (Zhangjiakou)'
['mirrors.registry.9']='Alibaba Cloud (Hohhot)'
['mirrors.registry.10']='Alibaba Cloud (Ulanqab)'
['mirrors.registry.11']='Alibaba Cloud (Shenzhen)'
['mirrors.registry.12']='Alibaba Cloud (Heyuan)'
['mirrors.registry.13']='Alibaba Cloud (Guangzhou)'
['mirrors.registry.14']='Alibaba Cloud (Chengdu)'
['mirrors.registry.15']='Alibaba Cloud (Hong Kong)'
['mirrors.registry.16']='Alibaba Cloud (Japan - Tokyo)'
['mirrors.registry.17']='Alibaba Cloud (Singapore)'
['mirrors.registry.18']='Alibaba Cloud (Malaysia - Kuala Lumpur)'
['mirrors.registry.19']='Alibaba Cloud (Indonesia - Jakarta)'
['mirrors.registry.20']='Alibaba Cloud (Germany - Frankfurt)'
['mirrors.registry.21']='Alibaba Cloud (UK - London)'
['mirrors.registry.22']='Alibaba Cloud (US West - Silicon Valley)'
['mirrors.registry.23']='Alibaba Cloud (US East - Virginia)'
['mirrors.registry.24']='Alibaba Cloud (UAE - Dubai)'
['mirrors.registry.25']='Tencent Cloud'
['mirrors.registry.26']='Google Cloud (North America)'
['mirrors.registry.27']='Google Cloud (Asia)'
['mirrors.registry.28']='Google Cloud (Europe)'
['mirrors.registry.29']='Official Docker Hub'
)
SPONSOR_ADS=(
"1Panel · Top-Rated Web-based Linux Server Management Tool ➜ \033[3mhttps://1panel.cn\033[0m"
)
}
init_msg_pack
handle_command_options "$@"
main