#!/bin/bash ## Author: SuperManito ## Modified: 2024-01-31 ## License: MIT ## GitHub: https://github.com/SuperManito/LinuxMirrors ## Website: https://linuxmirrors.cn ## Docker Registry 镜像仓库列表 # 格式:"软件源名称@软件源地址" mirror_list_registry=( "1panel.live@docker.1panel.live" "rat@hub.rat.dev" "1ms@docker.1ms.run" "1panel.top@docker.1panel.top" "k-docker@k-docker.asia" "func@func.ink" "proxy.1panel@proxy.1panel.live" "unsee@docker-0.unsee.tech" "zhai@docker.zhai.cm" "ussh@a.ussh.net" "melikeme@docker.melikeme.cn" "lispy@lispy.org" "hlmirror@docker.hlmirror.com" "xiaogenban1993@docker.xiaogenban1993.com" "kejilion@docker.kejilion.pro" "dockerpull@dockerpull.cn" "xuanyuan@docker.xuanyuan.me" "anye@docker.anye.in" "fast360@hub.fast360.xyz" ) ## 定义拉取的镜像,使用一个很小的镜像来测试速度 IMAGE="xjxjin/alist-sync" ## 定义系统判定变量 SYSTEM_DEBIAN="Debian" SYSTEM_UBUNTU="Ubuntu" SYSTEM_KALI="Kali" SYSTEM_REDHAT="RedHat" SYSTEM_RHEL="RedHat" SYSTEM_CENTOS="CentOS" SYSTEM_CENTOS_STREAM="CentOS Stream" SYSTEM_ROCKY="Rocky" SYSTEM_ALMALINUX="AlmaLinux" SYSTEM_FEDORA="Fedora" SYSTEM_OPENCLOUDOS="OpenCloudOS" SYSTEM_OPENEULER="openEuler" ## 定义目录和文件 File_LinuxRelease=/etc/os-release File_RedHatRelease=/etc/redhat-release File_OpenCloudOSRelease=/etc/opencloudos-release File_openEulerRelease=/etc/openEuler-release File_DebianVersion=/etc/debian_version File_DebianSourceList=/etc/apt/sources.list Dir_DebianExtendSource=/etc/apt/sources.list.d Dir_YumRepos=/etc/yum.repos.d ## 定义 Docker 相关变量 DockerDir=/etc/docker DockerConfig=$DockerDir/daemon.json DockerConfigBackup=$DockerDir/daemon.json.bak DockerVersionFile=docker-version.txt DockerCEVersionFile=docker-ce-version.txt DockerCECLIVersionFile=docker-ce-cli-version.txt ## 定义颜色变量 RED='\033[31m' GREEN='\033[32m' YELLOW='\033[33m' BLUE='\033[34m' PLAIN='\033[0m' BOLD='\033[1m' SUCCESS='[\033[32mOK\033[0m]' COMPLETE='[\033[32mDONE\033[0m]' WARN='[\033[33mWARN\033[0m]' ERROR='[\033[31mERROR\033[0m]' WORKING='[\033[34m*\033[0m]' function StartTitle() { [[ -z "${SOURCE}" || -z "${SOURCE_REGISTRY}" ]] && clear # 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 ' +-----------------------------------+' text_art=$(cat <<'EOF' .. .... .:----=: ...:::---==-:::-+. ..:=====-=+=-::::::== .:-. .-==*=-:::::::::::::::=*-: .:-=++. .-==++++-::::::::::::::-++:-==:. .=-=::=-. ....:::-=-::-++-:::::::::::::::--:::::==: -:.:=..+: ==-------::::-==-:::::::::::::::::::::::-+-. .=: .:=-.. ==-::::+-:::::==-:::::::::::::::::::::::::=+.:+- :-: :--==+*::::::-=-::::::::::::::::::::::::::-*+: .+. ..-*:::::::==::::::::::::::::::::::::::-+. -+. -*:::::::-=-:::::::--:::::::::::::::=-. +- :*::::::::-=::::::-=:::::=:::::::::-: .*. .+=:::::::::::::::-::::-*-::......:: -- :+::-:::::::::::::::::*=:-::...... -. :-:-===-:::::::::::.:+==--:...... .+. .==:...-+#+::....... . ....... .=- -*.....::............::-. ...=- .==-:.. :=-::::::=. ..:+- .:--===---=-:::-:::--:. ..:+: =--+=:+*+:. ...... ..-+. .#. .+#- .:. .::=: -=:.-: ..::-. .-=. xjxjin ...:-: ... ...:- EOF ) echo "$text_art" echo -e ' 欢迎使用 xjxjin Docker 一键测速换源脚本' } ## 报错退出 function Output_Error() { [ "$1" ] && echo -e "\n$ERROR $1\n" exit 1 } ## 基础环境判断 function PermissionJudgment() { if [ $UID -ne 0 ]; then echo -e "\n$ERROR 权限不足,请使用 Root 用户运行本脚本\n" exit 1 fi } ## 检查Docker是否安装 function CheckInstallDocker() { if ! docker --version &> /dev/null; then echo -e "\n$ERROR docker 未安装,请先安装docker\n" exit 1 fi } ## 系统判定变量 function EnvJudgment() { ## 定义系统名称 SYSTEM_NAME="$(cat $File_LinuxRelease | grep -E "^NAME=" | awk -F '=' '{print$2}' | sed "s/[\'\"]//g")" cat $File_LinuxRelease | grep "PRETTY_NAME=" -q [ $? -eq 0 ] && SYSTEM_PRETTY_NAME="$(cat $File_LinuxRelease | grep -E "^PRETTY_NAME=" | awk -F '=' '{print$2}' | sed "s/[\'\"]//g")" ## 定义系统版本号 SYSTEM_VERSION_NUMBER="$(cat $File_LinuxRelease | grep -E "^VERSION_ID=" | awk -F '=' '{print$2}' | sed "s/[\'\"]//g")" ## 定义系统ID SYSTEM_ID="$(cat $File_LinuxRelease | grep -E "^ID=" | awk -F '=' '{print$2}' | sed "s/[\'\"]//g")" ## 判定当前系统派系 if [ -s $File_DebianVersion ]; then SYSTEM_FACTIONS="${SYSTEM_DEBIAN}" elif [ -s $File_openEulerRelease ]; then SYSTEM_FACTIONS="${SYSTEM_OPENEULER}" elif [ -s $File_RedHatRelease ]; then SYSTEM_FACTIONS="${SYSTEM_REDHAT}" elif [ -s $File_OpenCloudOSRelease ]; then SYSTEM_FACTIONS="${SYSTEM_OPENCLOUDOS}" # 注:RedHat 判断优先级需要高于 OpenCloudOS,因为8版本基于红帽而9版本不是 else Output_Error "无法判断当前运行环境,当前系统不在本脚本的支持范围内" fi ## 判定系统名称、版本、版本号 case "${SYSTEM_FACTIONS}" in "${SYSTEM_DEBIAN}") if [ ! -x /usr/bin/lsb_release ]; then apt-get install -y lsb-release if [ $? -ne 0 ]; then Output_Error "lsb-release 软件包安装失败\n 本脚本需要通过 lsb_release 指令判断系统具体类型和版本,当前系统可能为精简安装,请自行安装后重新执行脚本!" fi fi SYSTEM_JUDGMENT="$(lsb_release -is)" SYSTEM_VERSION_CODENAME="${DEBIAN_CODENAME:-"$(lsb_release -cs)"}" ;; "${SYSTEM_REDHAT}") SYSTEM_JUDGMENT="$(cat $File_RedHatRelease | awk -F ' ' '{printf$1}')" ## Red Hat Enterprise Linux cat $File_RedHatRelease | grep -q "${SYSTEM_RHEL}" [ $? -eq 0 ] && SYSTEM_JUDGMENT="${SYSTEM_RHEL}" ## CentOS Stream cat $File_RedHatRelease | grep -q "${SYSTEM_CENTOS_STREAM}" [ $? -eq 0 ] && SYSTEM_JUDGMENT="${SYSTEM_CENTOS_STREAM}" ;; "${SYSTEM_OPENCLOUDOS}") SYSTEM_JUDGMENT="${SYSTEM_OPENCLOUDOS}" ;; "${SYSTEM_OPENEULER}") SYSTEM_JUDGMENT="${SYSTEM_OPENEULER}" ;; esac ## 判定系统处理器架构 case $(uname -m) in x86_64) SYSTEM_ARCH="x86_64" SOURCE_ARCH="amd64" ;; aarch64) SYSTEM_ARCH="ARM64" SOURCE_ARCH="arm64" ;; armv7l) SYSTEM_ARCH="ARMv7" SOURCE_ARCH="armhf" ;; armv6l) SYSTEM_ARCH="ARMv6" SOURCE_ARCH="armhf" ;; i386 | i686) Output_Error "Docker Engine 不支持安装在 x86_32 架构的环境上!" ;; *) SYSTEM_ARCH=$(uname -m) SOURCE_ARCH=armhf ;; esac ## 定义软件源分支名称 case "${SYSTEM_JUDGMENT}" in "${SYSTEM_CENTOS}" | "${SYSTEM_DEBIAN}" | "${SYSTEM_UBUNTU}" | "${SYSTEM_FEDORA}") SOURCE_BRANCH="$(echo "${SYSTEM_JUDGMENT,,}" | sed "s/ /-/g")" ;; "${SYSTEM_CENTOS_STREAM}" | "${SYSTEM_ALMALINUX}" | "${SYSTEM_ROCKY}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_OPENEULER}") SOURCE_BRANCH="centos" ;; "${SYSTEM_RHEL}") SOURCE_BRANCH="rhel" ;; "${SYSTEM_KALI}") # Kali 使用 Debian 12 的 docker ce 源 SOURCE_BRANCH="debian" SYSTEM_VERSION_CODENAME="bullseye" ;; *) Output_Error "当前系统不在本脚本的支持范围内" ;; esac ## 定义软件源更新文字 case "${SYSTEM_FACTIONS}" in "${SYSTEM_DEBIAN}") SYNC_MIRROR_TEXT="更新软件源" ;; "${SYSTEM_REDHAT}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_OPENEULER}") SYNC_MIRROR_TEXT="生成软件源缓存" ;; esac } # 检查是否安装了bc,如果没有安装,则进行安装 CheckBc() { if ! command -v bc &> /dev/null; then echo "找不到bc,正在尝试安装它。。。" sudo apt-get update if ! sudo apt-get install -y bc; then echo "Failed to install bc. Please install it manually and rerun the script." exit 1 fi fi } # 定义一个函数来测试镜像源的下载速度 test_speed() { local source=$1 # local image="xjxjin/alist-sync" # 使用一个很小的镜像来测试速度 # 拉取镜像前记录当前时间 SECONDS=0 # 使用指定的镜像源拉取xjxjin/alist-sync镜像 docker rmi $source/$IMAGE > /dev/null 2>&1 #timeout 300 docker pull $source/$IMAGE > /dev/null 2>&1 docker pull $source/$IMAGE > /dev/null 2>&1 local status=$? if [ $status -eq 124 ]; then # 超时错误 # echo "Docker pull timed out, exit status: $status" docker rmi "$source/$image" > /dev/null 2>&1 echo "300" return elif [ $status -ne 0 ]; then # 其他错误 # echo "Docker pull failed, exit status: $status" docker rmi "$source/$image" > /dev/null 2>&1 echo "300" return fi docker rmi $source/$IMAGE > /dev/null 2>&1 # 计算下载时间(秒) echo $SECONDS } # 定义一个函数,用于等待一个进程在给定的时间内完成,否则返回超时 # wait_timeout() { # local pid=$1 # local timeout=$2 # local kill_signal=TERM # 默认发送TERM信号 # # 使用timeout命令等待子进程结束,或者超时后发送信号 # timeout $timeout bash -c "while kill -0 $pid 2>/dev/null; do sleep 1; done" || kill -$kill_signal $pid # wait $pid 2>/dev/null # 等待进程真正结束,忽略任何错误 # return $? # 返回子进程的退出状态 # } # 定义一个函数来测试镜像源的下载速度 # test_speed() { # local source=$1 # local image="xjxjin/alist-sync:1.0.1" # local start_time # local timeout_seconds=30 # # 拉取镜像前记录当前时间 # start_time=$(date +%s.%N) # # 将docker pull放入后台运行 # docker pull "$source/$image" > /dev/null 2>&1 & # local pull_pid=$! # 获取后台进程的PID # # 等待docker pull完成或者超时 # if ! wait_timeout $pull_pid $timeout_seconds; then # # 如果超时,则设置download_time为30秒(作为超时指示) # local download_time=30 # echo "下载超时" # else # # 拉取镜像后记录当前时间 # local end_time=$(date +%s.%N) # # 计算下载时间(秒) # local download_time=$(echo "$end_time - $start_time" | bc -l) # fi # echo "$download_time" # } function ChooseMirrors() { ## 打印软件源列表 function PrintMirrorsList() { local tmp_mirror_name tmp_mirror_url arr_num default_mirror_name_length tmp_mirror_name_length tmp_spaces_nums a i j ## 计算字符串长度 function StringLength() { local text=$1 echo "${#text}" } echo -e '' local list_arr=() local list_arr_sum="$(eval echo \${#$1[@]})" for ((a = 0; a < $list_arr_sum; a++)); do list_arr[$a]="$(eval echo \${$1[a]})" done if [ -x /usr/bin/printf ]; then for ((i = 0; i < ${#list_arr[@]}; i++)); do tmp_mirror_name=$(echo "${list_arr[i]}" | awk -F '@' '{print$1}') # 软件源名称 # tmp_mirror_url=$(echo "${list_arr[i]}" | awk -F '@' '{print$2}') # 软件源地址 arr_num=$((i + 1)) default_mirror_name_length=${2:-"30"} # 默认软件源名称打印长度 ## 补齐长度差异(中文的引号在等宽字体中占1格而非2格) [[ $(echo "${tmp_mirror_name}" | grep -c "“") -gt 0 ]] && let default_mirror_name_length+=$(echo "${tmp_mirror_name}" | grep -c "“") [[ $(echo "${tmp_mirror_name}" | grep -c "”") -gt 0 ]] && let default_mirror_name_length+=$(echo "${tmp_mirror_name}" | grep -c "”") [[ $(echo "${tmp_mirror_name}" | grep -c "‘") -gt 0 ]] && let default_mirror_name_length+=$(echo "${tmp_mirror_name}" | grep -c "‘") [[ $(echo "${tmp_mirror_name}" | grep -c "’") -gt 0 ]] && let default_mirror_name_length+=$(echo "${tmp_mirror_name}" | grep -c "’") # 非一般字符长度 tmp_mirror_name_length=$(StringLength $(echo "${tmp_mirror_name}" | sed "s| ||g" | sed "s|[0-9a-zA-Z\.\=\:\_\(\)\'\"-\/\!·]||g;")) ## 填充空格 tmp_spaces_nums=$(($(($default_mirror_name_length - ${tmp_mirror_name_length} - $(StringLength "${tmp_mirror_name}"))) / 2)) for ((j = 1; j <= ${tmp_spaces_nums}; j++)); do tmp_mirror_name="${tmp_mirror_name} " done printf " ❖ %-$(($default_mirror_name_length + ${tmp_mirror_name_length}))s %4s\n" "${tmp_mirror_name}" "$arr_num)" done else for ((i = 0; i < ${#list_arr[@]}; i++)); do tmp_mirror_name=$(echo "${list_arr[i]}" | awk -F '@' '{print$1}') # 软件源名称 tmp_mirror_url=$(echo "${list_arr[i]}" | awk -F '@' '{print$2}') # 软件源地址 arr_num=$((i + 1)) echo -e " ❖ $arr_num. ${tmp_mirror_url} | ${tmp_mirror_name}" done fi } function Title() { local system_name="${SYSTEM_PRETTY_NAME:-"${SYSTEM_NAME} ${SYSTEM_VERSION_NUMBER}"}" local arch=""${DEVICE_ARCH}"" local date="$(date "+%Y-%m-%d %H:%M:%S")" local timezone="$(timedatectl status 2>/dev/null | grep "Time zone" | awk -F ':' '{print$2}' | awk -F ' ' '{print$1}')" echo -e '' echo -e " 运行环境 ${BLUE}${system_name} ${arch}${PLAIN}" echo -e " 系统时间 ${BLUE}${date} ${timezone}${PLAIN}" echo -e " 国内环境可用docker镜像源上报: https://gitee.com/xjxjin/scripts/issues" echo -e " 国际环境可用docker镜像源上报: https://github.com/xjxjin/scripts/issues" echo -e " 2025-03-08 新增可用docker源17个,剔除部分无效源" } Title local mirror_list_name if [[ -z "${SOURCE_REGISTRY}" ]]; then mirror_list_name="mirror_list_registry" PrintMirrorsList "${mirror_list_name}" 44 local CHOICE_C=$(echo -e "\n${BOLD}└─ 请选择并输入你想使用的 Docker Registry 源 [ 1-$(eval echo \${#$mirror_list_name[@]}) ]:${PLAIN}") # 初始化一个数组来存储每个源的速度 declare -A speed_array declare -A speed_index_array # 用来存储每个源在列表中的索引 local fastest_mirrors=() # 测试每个源的速度并打印结果 echo -e "\n${BOLD}测试中,可能需要10min, 请稍候...${PLAIN}" local tmp_mirror_name tmp_mirror_url arr_num default_mirror_name_length tmp_mirror_name_length tmp_spaces_nums a i j ## 计算字符串长度 echo -e '' local list_arr=() local list_arr_sum="$(eval echo \${#${mirror_list_name}[@]})" for ((a = 0; a < $list_arr_sum; a++)); do list_arr[$a]="$(eval echo \${${mirror_list_name}[a]})" done if [ -x /usr/bin/printf ]; then for ((i = 0; i < ${#list_arr[@]}; i++)); do tmp_mirror_name=$(echo "${list_arr[i]}" | awk -F '@' '{print$1}') # 软件源名称 tmp_mirror_url=$(echo "${list_arr[i]}" | awk -F '@' '{print$2}') # 软件源地址 local speed=$(test_speed "$tmp_mirror_url") speed_array[$tmp_mirror_name]=$speed speed_index_array[$tmp_mirror_name]=$((i+1)) # local speed=$(test_speed "$tmp_mirror_url") arr_num=$((i + 1)) default_mirror_name_length=${2:-"30"} # 默认软件源名称打印长度 ## 补齐长度差异(中文的引号在等宽字体中占1格而非2格) [[ $(echo "${tmp_mirror_name}" | grep -c "“") -gt 0 ]] && let default_mirror_name_length+=$(echo "${tmp_mirror_name}" | grep -c "“") [[ $(echo "${tmp_mirror_name}" | grep -c "”") -gt 0 ]] && let default_mirror_name_length+=$(echo "${tmp_mirror_name}" | grep -c "”") [[ $(echo "${tmp_mirror_name}" | grep -c "‘") -gt 0 ]] && let default_mirror_name_length+=$(echo "${tmp_mirror_name}" | grep -c "‘") [[ $(echo "${tmp_mirror_name}" | grep -c "’") -gt 0 ]] && let default_mirror_name_length+=$(echo "${tmp_mirror_name}" | grep -c "’") # 非一般字符长度 tmp_mirror_name_length=$(StringLength $(echo "${tmp_mirror_name}" | sed "s| ||g" | sed "s|[0-9a-zA-Z\.\=\:\_\(\)\'\"-\/\!·]||g;")) ## 填充空格 tmp_spaces_nums=$(($(($default_mirror_name_length - ${tmp_mirror_name_length} - $(StringLength "${tmp_mirror_name}"))) / 2)) for ((j = 1; j <= ${tmp_spaces_nums}; j++)); do tmp_mirror_name="${tmp_mirror_name} " done # speed_index_array[$tmp_mirror_name]=$((i+1)) # 存储源的id值 #if (( $(echo "scale=1; $speed < 1" | bc -l) )); then #printf "源%2s %-$(($default_mirror_name_length + ${tmp_mirror_name_length}))s 下载速度: %s 秒\n" "$arr_num" "${tmp_mirror_name}" "0${speed}" # speed_index_array[$source]=$((i+1)) #speed_index_array[$tmp_mirror_name]="$(printf "源%2s %-$(($default_mirror_name_length + ${tmp_mirror_name_length}))s \n" "$arr_num" "${tmp_mirror_name}")" #else speed_index_array[$tmp_mirror_name]="$(printf "源%2s %-$(($default_mirror_name_length + ${tmp_mirror_name_length}))s \n" "$arr_num" "${tmp_mirror_name}")" printf "源%2s %-$(($default_mirror_name_length + ${tmp_mirror_name_length}))s 下载速度: %s 秒\n" "$arr_num" "${tmp_mirror_name}" "${speed}" #fi done else for ((i = 0; i < ${#list_arr[@]}; i++)); do tmp_mirror_name=$(echo "${list_arr[i]}" | awk -F '@' '{print$1}') # 软件源名称 tmp_mirror_url=$(echo "${list_arr[i]}" | awk -F '@' '{print$2}') # 软件源地址 arr_num=$((i + 1)) echo -e " ❖ $arr_num. ${tmp_mirror_url} | ${tmp_mirror_name}" done fi # 使用sort命令的-k选项对速度和源的配对进行排序,-t选项定义字段分隔符为空格 # 注意:这里我们使用process substitution <(...)来避免使用临时文件 sorted_pairs=$(for source in "${!speed_array[@]}"; do echo "${speed_array[$source]} $source ${speed_index_array[$source]}"; done | sort -k1,1n -t' ' -s) # 提取速度最小的三个源 local count=0 echo "" echo "速度最快的三个源:" while IFS=' ' read -r speed tmp_mirror_name arr_num && [[ $count -lt 3 ]]; do default_mirror_name_length=${2:-"30"} # 默认软件源名称打印长度 ## 补齐长度差异(中文的引号在等宽字体中占1格而非2格) [[ $(echo "${tmp_mirror_name}" | grep -c "“") -gt 0 ]] && let default_mirror_name_length+=$(echo "${tmp_mirror_name}" | grep -c "“") [[ $(echo "${tmp_mirror_name}" | grep -c "”") -gt 0 ]] && let default_mirror_name_length+=$(echo "${tmp_mirror_name}" | grep -c "”") [[ $(echo "${tmp_mirror_name}" | grep -c "‘") -gt 0 ]] && let default_mirror_name_length+=$(echo "${tmp_mirror_name}" | grep -c "‘") [[ $(echo "${tmp_mirror_name}" | grep -c "’") -gt 0 ]] && let default_mirror_name_length+=$(echo "${tmp_mirror_name}" | grep -c "’") # 非一般字符长度 tmp_mirror_name_length=$(StringLength $(echo "${tmp_mirror_name}" | sed "s| ||g" | sed "s|[0-9a-zA-Z\.\=\:\_\(\)\'\"-\/\!·]||g;")) ## 填充空格 tmp_spaces_nums=$(($(($default_mirror_name_length - ${tmp_mirror_name_length} - $(StringLength "${tmp_mirror_name}"))) / 2)) for ((j = 1; j <= ${tmp_spaces_nums}; j++)); do tmp_mirror_name="${tmp_mirror_name} " done # if (( $(echo "scale=1; $speed < 1" | bc -l) )); then #printf "源%2s %-$(($default_mirror_name_length + ${tmp_mirror_name_length}))s ${RED}下载速度: %s${PLAIN} 秒\n" "$arr_num" "${tmp_mirror_name}" "0${speed}" #else printf "源%2s %-$(($default_mirror_name_length + ${tmp_mirror_name_length}))s ${RED}下载速度: %s${PLAIN} 秒\n" "$arr_num" "${tmp_mirror_name}" "${speed}" #fi ((count++)) done <<< "$sorted_pairs" while true; do read -p "${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 请输入有效的数字序号!" else SOURCE_REGISTRY="$(eval echo \${${mirror_list_name}[$(($INPUT - 1))]} | awk -F '@' '{print$2}')" echo "${SOURCE_REGISTRY}" # exit break fi ;; *) echo -e "\n$WARN 请输入数字序号以选择你想使用的软件源!" ;; esac done fi } ## 关闭防火墙和SELinux function CloseFirewall() { if [ ! -x /usr/bin/systemctl ]; then return fi if [[ "$(systemctl is-active firewalld)" == "active" ]]; then if [[ -z "${CLOSE_FIREWALL}" ]]; then local CHOICE CHOICE=$(echo -e "\n${BOLD}└─ 是否关闭防火墙和 SELinux ? [Y/n] ${PLAIN}") read -rp "${CHOICE}" INPUT [[ -z "${INPUT}" ]] && INPUT=Y case "${INPUT}" in [Yy] | [Yy][Ee][Ss]) CLOSE_FIREWALL="true" ;; [Nn] | [Nn][Oo]) ;; *) echo -e "\n$WARN 输入错误,默认不关闭!" ;; esac 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 } ## 安装 Docker Engine function DockerEngine() { ## 修改 Docker Registry 源 function RegistryMirror() { if [[ "${REGISTRY_SOURCEL}" != "registry.hub.docker.com" ]]; then if [ -d $DockerDir ] && [ -e $DockerConfig ]; then if [ -e $DockerConfigBackup ]; then if [[ "${IGNORE_BACKUP_TIPS}" == "false" ]]; then local CHOICE_BACKUP=$(echo -e "\n${BOLD}└─ 检测到已备份的 Docker 配置文件,是否跳过覆盖备份? [Y/n] ${PLAIN}") read -p "${CHOICE_BACKUP}" INPUT [[ -z "${INPUT}" ]] && INPUT=Y case $INPUT in [Yy] | [Yy][Ee][Ss]) ;; [Nn] | [Nn][Oo]) echo '' cp -rvf $DockerConfig $DockerConfigBackup 2>&1 ;; *) echo -e "\n$WARN 输入错误,默认不覆盖!" ;; esac fi else cp -rvf $DockerConfig $DockerConfigBackup 2>&1 echo -e "\n$COMPLETE 已备份原有 Docker 配置文件至 $DockerConfigBackup\n" fi sleep 2s else mkdir -p $DockerDir >/dev/null 2>&1 touch $DockerConfig fi echo -e '{\n "registry-mirrors": ["https://SOURCE"]\n}' >$DockerConfig sed -i "s|SOURCE|${SOURCE_REGISTRY}|g" $DockerConfig systemctl daemon-reload fi } RegistryMirror systemctl stop docker >/dev/null 2>&1 systemctl enable --now docker >/dev/null 2>&1 } ## 重启Docker function RestartDocker() { # 询问用户是否需要重启Docker服务以应用配置更改 local CHOICE_BACKUP=$(echo -e "\n${BOLD}└─ 是否重启Docker以使配置文件生效? [Y/n] ${PLAIN}") read -p "${CHOICE_BACKUP}" INPUT [[ -z "${INPUT}" ]] && INPUT=Y case $INPUT in [Yy]|[Yy][Ee][Ss]) echo '' echo "重启Docker服务以应用配置更改..." systemctl restart docker echo "重启Docker服务完成" ;; [Nn]|[Nn][Oo]) echo "Docker服务未重启。配置更改将不会立即生效。" ;; *) # echo "未知输入,Docker服务未重启。配置更改将不会立即生效。" echo "Docker服务未重启。配置更改将不会立即生效。" ;; esac } ## 查看版本并验证安装结果 function CheckVersion() { if [ -x /usr/bin/docker ]; then echo -en "\n验证安装版本:" docker -v VERIFICATION_DOCKER=$? if [ ${VERIFICATION_DOCKER} -eq 0 ]; then echo -e "\n$COMPLETE 安装完成" else echo -e "\n$ERROR 安装失败" case "${SYSTEM_FACTIONS}" in "${SYSTEM_DEBIAN}") echo -e "\n检查源文件:cat $Dir_DebianExtendSource/docker.list" echo -e '请尝试手动执行安装命令: apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin\n' echo '' ;; "${SYSTEM_REDHAT}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_OPENEULER}") echo -e "\n检查源文件:cat $Dir_YumRepos/docker.repo" echo -e '请尝试手动执行安装命令: yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin\n' ;; esac exit 1 fi if [[ $(systemctl is-active docker) != "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$ERROR 检测到 Docker 服务启动异常,可能由于重复安装导致" echo -e "\n${YELLOW} 请执行 "systemctl start docker" 或 "service docker start" 命令尝试启动,如若报错请尝试重新执行本脚本${PLAIN}" fi fi else echo -e "\n$ERROR 安装失败\n" fi } ## 运行结束 function RunEnd() { echo -e "\n ------ 脚本执行结束 ------" # echo -e ' \033[0;1;35;95m┌─\033[0;1;31;91m──\033[0;1;33;93m──\033[0;1;32;92m──\033[0;1;36;96m──\033[0;1;34;94m──\033[0;1;35;95m──\033[0;1;31;91m──\033[0;1;33;93m──\033[0;1;32;92m──\033[0;1;36;96m──\033[0;1;34;94m──\033[0;1;35;95m──\033[0;1;31;91m──\033[0;1;33;93m──\033[0;1;32;92m──\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[0;1;36;96m▌\033[0m \033[0;1;31;91m▗\033[0;1;33;93m▐\033[0m \033[0;1;34;94m│\033[0m' # echo -e ' \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[0;1;35;95m▀▖\033[0;1;31;91m▞▀\033[0;1;33;93m▖▙\033[0;1;32;92m▀▖\033[0;1;36;96m▌▘\033[0;1;34;94m▌▝\033[0;1;35;95m▀▖\033[0;1;31;91m▛▀\033[0;1;33;93m▖▄\033[0;1;32;92m▜▀\033[0m \033[0;1;36;96m▞\033[0;1;34;94m▀▖\033[0;1;35;95m│\033[0m' # echo -e ' \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[0;1;31;91m▄▘\033[0;1;33;93m▛▀\033[0m \033[0;1;32;92m▌\033[0m \033[0;1;34;94m▌\033[0m \033[0;1;35;95m▌▞\033[0;1;31;91m▀▌\033[0;1;33;93m▌\033[0m \033[0;1;32;92m▌▐\033[0;1;36;96m▐\033[0m \033[0;1;34;94m▖▌\033[0m \033[0;1;35;95m▌\033[0;1;31;91m│\033[0m' # echo -e ' \033[0;1;36;96m│▝\033[0;1;34;94m▀\033[0m \033[0;1;35;95m▝▀\033[0;1;31;91m▘▌\033[0m \033[0;1;32;92m▝▀\033[0;1;36;96m▘▘\033[0m \033[0;1;35;95m▘\033[0m \033[0;1;31;91m▘▝\033[0;1;33;93m▀▘\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[0;1;31;91m▀\033[0m \033[0;1;33;93m│\033[0m' # echo -e ' \033[0;1;34;94m└─\033[0;1;35;95m──\033[0;1;31;91m──\033[0;1;33;93m──\033[0;1;32;92m──\033[0;1;36;96m──\033[0;1;34;94m──\033[0;1;35;95m──\033[0;1;31;91m──\033[0;1;33;93m──\033[0;1;32;92m──\033[0;1;36;96m──\033[0;1;34;94m──\033[0;1;35;95m──\033[0;1;31;91m──\033[0;1;33;93m──\033[0;1;32;92m┘\033[0m' # echo -e " \033[1;34mPowered by linuxmirrors.cn\033[0m\n" } function PrintXJXJin() { echo -e "\n ------ xjxjin ------" echo -e ' \033[0;1;35m┌─\033[0;1;31m──\033[0;1;33m──\033[0;1;32m──\033[0;1;36m──\033[0;1;34m──\033[0;1;35m──\033[0;1;31m──\033[0;1;33m──\033[0;1;32m──\033[0;1;36m──\033[0;1;34m──\033[0;1;35m──\033[0;1;31m──\033[0;1;33m──\033[0;1;32m──\033[0m' echo -e ' \033[0;1;31m│\033[0;1;33m▓\033[0m\033[0;1;32mX\033[0;1;36mj\033[0;1;34mx\033[0;1;35mJ\033[0;1;31mx\033[0;1;33m▓\033[0m \033[0;1;34m│' echo -e ' \033[0;1;33m│\033[0;1;32mx\033[0;1;36mj\033[0;1;34mX\033[0;1;35mj\033[0;1;31mX\033[0;1;33mj\033[0;1;32mx\033[0m \033[0;1;36m▓\033[0;1;34m│' echo -e ' \033[0;1;32m│\033[0;1;36m▓\033[0m \033[0;1;34mX\033[0;1;35mj\033[0;1;31mX\033[0;1;33mj\033[0;1;32mX\033[0;1;36mj\033[0m \033[0;1;34m▓\033[0m \033[0;1;32m│' echo -e ' \033[0;1;36m│\033[0;1;34m▓\033[0m \033[0;1;35mX\033[0;1;31mj\033[0;1;33mX\033[0;1;32mj\033[0;1;36mX\033[0;1;34mj\033[0m \033[0;1;35m▓\033[0m \033[0;1;36m│' echo -e ' \033[0;1;34m└─\033[0;1;35m──\033[0;1;31m──\033[0;1;33m──\033[0;1;32m──\033[0;1;36m──\033[0;1;34m──\033[0;1;35m──\033[0;1;31m──\033[0;1;33m──\033[0;1;32m──\033[0;1;36m──\033[0;1;34m──\033[0;1;35m──\033[0;1;31m──\033[0;1;33m──\033[0;1;32m──\033[0m' echo -e " \033[1;34mPowered by linuxmirrors.cn\033[0m\n" } ## 处理命令选项 function CommandOptions() { ## 命令帮助 function Output_Help_Info() { echo -e " 命令选项(参数名/含义/参数值): --source 指定 Docker CE 源地址 地址 --source-registry 指定 Docker Registry 源地址 地址 --codename 指定 Debian 系操作系统的版本代号 代号名称 --install-latested 控制是否安装最新版本的 Docker Engine true 或 false --ignore-backup-tips 忽略覆盖备份提示 无 问题报告 https://github.com/SuperManito/LinuxMirrors/issues " } ## 判断参数 while [ $# -gt 0 ]; do case "$1" in ## 指定 Docker CE 软件源地址 --source) if [ "$2" ]; then echo "$2" | grep -Eq "\(|\)|\[|\]|\{|\}" if [ $? -eq 0 ]; then Output_Error "检测到无效参数值 ${BLUE}$2${PLAIN} ,请输入有效的地址!" else SOURCE="$(echo "$2" | sed -e 's,^http[s]\?://,,g' -e 's,/$,,')" shift fi else Output_Error "检测到 ${BLUE}$1${PLAIN} 为无效参数,请在该参数后指定软件源地址!" fi ;; ## 指定 Docker Registry 仓库地址 --source-registry) if [ "$2" ]; then echo "$2" | grep -Eq "\(|\)|\[|\]|\{|\}" if [ $? -eq 0 ]; then Output_Error "检测到无效参数值 ${BLUE}$2${PLAIN} ,请输入有效的地址!" else SOURCE_REGISTRY="$(echo "$2" | sed -e 's,^http[s]\?://,,g' -e 's,/$,,')" shift fi else Output_Error "检测到 ${BLUE}$1${PLAIN} 为无效参数,请在该参数后指定软件源地址!" fi ;; ## 指定 Debian 版本代号 --codename) if [ "$2" ]; then DEBIAN_CODENAME="$2" shift else Output_Error "检测到 ${BLUE}$1${PLAIN} 为无效参数,请在该参数后指定版本代号!" fi ;; ## 安装最新版本 --install-latested) if [ "$2" ]; then case "$2" in [Tt]rue | [Ff]alse) INSTALL_LATESTED_DOCKER="${2,,}" shift ;; *) Output_Error "检测到 ${BLUE}$2${PLAIN} 为无效参数值,请在该参数后指定 true 或 false 作为参数值!" ;; esac else Output_Error "检测到 ${BLUE}$1${PLAIN} 为无效参数,请在该参数后指定 true 或 false 作为参数值!" fi ;; ## 忽略覆盖备份提示 --ignore-backup-tips) IGNORE_BACKUP_TIPS="true" ;; ## 命令帮助 --help) Output_Help_Info exit ;; *) Output_Error "检测到 ${BLUE}$1${PLAIN} 为无效参数,请确认后重新输入!" ;; esac shift done ## 给部分命令选项赋予默认值 IGNORE_BACKUP_TIPS="${IGNORE_BACKUP_TIPS:-"false"}" } ## 组合函数 function Combin_Function() { ## 检查Docker是否安装 CheckInstallDocker ## 基础环境判断 PermissionJudgment ## 系统判定变量 # EnvJudgment ## 检查bc是否安装 # CheckBc ## 欢迎使用 Docker Engine 一键安装脚本 StartTitle ## 打印软件源列表 ChooseMirrors ## 关闭防火墙和SELinux CloseFirewall ## 安装 Docker Engine DockerEngine # 重启docker RestartDocker ## 查看版本并验证安装结果 CheckVersion ## 运行结束 RunEnd } CommandOptions "$@" Combin_Function