diff --git a/ChangeMirrors.sh b/ChangeMirrors.sh index 66398a0..c8af353 100755 --- a/ChangeMirrors.sh +++ b/ChangeMirrors.sh @@ -1,6 +1,6 @@ #!/bin/bash ## Author: SuperManito -## Modified: 2025-04-01 +## Modified: 2025-04-05 ## License: MIT ## GitHub: https://github.com/SuperManito/LinuxMirrors ## Website: https://linuxmirrors.cn @@ -288,11 +288,12 @@ function handle_command_options() { --install-epel 是否安装 EPEL 附加软件包 true 或 false --backup 是否备份原有软件源 true 或 false --upgrade-software 是否更新软件包 true 或 false - --clean-cache 是否清理下载缓存 true 或 false + --clean-cache 是否在更新软件包后清理下载缓存 true 或 false --clean-screen 是否在运行前清除屏幕上的所有内容 true 或 false --only-epel 仅更换 EPEL 软件源模式 无 --ignore-backup-tips 忽略覆盖备份提示 无 --print-diff 打印源文件修改前后差异 无 + --pure-mode 纯净模式,精简打印内容 无 问题报告 https://github.com/SuperManito/LinuxMirrors/issues\n" } @@ -549,7 +550,7 @@ function handle_command_options() { output_error "命令选项 ${BLUE}$1${PLAIN} 无效,请在该选项后指定 true 或 false !" fi ;; - ## 清理下载缓存 + ## 在更新软件包后清理下载缓存 --clean-cache) if [ "$2" ]; then case "$2" in @@ -585,6 +586,10 @@ function handle_command_options() { --print-diff) PRINT_DIFF="true" ;; + ## 纯净模式 + --pure-mode) + PURE_MODE="true" + ;; ## 命令帮助 --help) output_command_help @@ -596,12 +601,13 @@ function handle_command_options() { esac shift done - ## 给部分命令选项赋予默认值 + ## 设置部分功能的默认值 ONLY_EPEL="${ONLY_EPEL:-"false"}" BACKUP="${BACKUP:-"true"}" USE_OFFICIAL_SOURCE="${USE_OFFICIAL_SOURCE:-"false"}" IGNORE_BACKUP_TIPS="${IGNORE_BACKUP_TIPS:-"false"}" PRINT_DIFF="${PRINT_DIFF:-"false"}" + PURE_MODE="${PURE_MODE:-"false"}" } function run_start() { @@ -610,14 +616,21 @@ function run_start() { elif [ "${CLEAN_SCREEN}" == "true" ]; then clear fi - echo -e '+-----------------------------------+' + if [[ "${PURE_MODE}" == "true" ]]; then + return + fi + 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 '欢迎使用 GNU/Linux 更换系统软件源脚本' + echo -e "+-----------------------------------+" + echo -e "欢迎使用 GNU/Linux 更换系统软件源脚本" } function run_end() { + if [[ "${PURE_MODE}" == "true" ]]; then + echo '' + return + fi echo -e "\n✨ 脚本运行完毕,更多使用教程详见官网 👉 \033[3mhttps://linuxmirrors.cn\033[0m\n\n🔥 1Panel · Linux 面板|极简运维 ➜ https://1panel.cn \033[3;2m【广告】\033[0m\n🔥 林枫云 · 专注独立IP高频VPS|R9/i9系列定制 ➜ https://www.dkdun.cn \033[3;2m【广告】\033[0m\n\n\033[3;1mPowered by \033[34mLinuxMirrors\033[0m\n" } @@ -721,7 +734,7 @@ function collect_system_info() { fi fi if [[ "${SYSTEM_VERSION_CODENAME}" == "sid" ]]; then - echo -e "\n${WARN} 检测到当前系统为 ${BLUE}unstable(sid)${PLAIN} 版本,可能会产生一些无法预料的问题。\n" + [[ "${PURE_MODE}" != "true" ]] && echo -e "\n${WARN} 检测到当前系统为 ${BLUE}unstable(sid)${PLAIN} 版本,可能会产生一些无法预料的问题。\n" fi ;; "${SYSTEM_UBUNTU}") @@ -1036,7 +1049,7 @@ function choose_mirrors() { interactive_select_boolean "${BOLD}默认使用软件源的公网地址,是否继续?${PLAIN}" if [[ "${_SELECT_RESULT}" == "false" ]]; then SOURCE="${intranet_source}" - echo -e "\n$WARN 已切换至内网专用地址,仅限在特定环境下使用!" + [[ "${PURE_MODE}" != "true" ]] && echo -e "\n$WARN 已切换至内网专用地址,仅限在特定环境下使用!" fi else local CHOICE=$(echo -e "\n${BOLD}└─ 默认使用软件源的公网地址,是否继续? [Y/n] ${PLAIN}") @@ -1046,7 +1059,7 @@ function choose_mirrors() { [Yy] | [Yy][Ee][Ss]) ;; [Nn] | [Nn][Oo]) SOURCE="${intranet_source}" - echo -e "\n$WARN 已切换至内网专用地址,仅限在特定环境下使用!" + [[ "${PURE_MODE}" != "true" ]] && echo -e "\n$WARN 已切换至内网专用地址,仅限在特定环境下使用!" ;; *) echo -e "\n$WARN 输入错误,默认不使用内网地址!" @@ -1070,7 +1083,8 @@ function choose_mirrors() { echo -e "系统时间 ${BLUE}${date_time} ${time_zone}${PLAIN}" } - print_title + [[ "${PURE_MODE}" != "true" ]] && print_title + if [[ -z "${SOURCE}" ]]; then ## 使用官方源 if [[ "${USE_OFFICIAL_SOURCE}" == "true" ]]; then @@ -1632,44 +1646,65 @@ function change_mirrors_main() { print_diff fi ## 更新软件源 - echo -e "\n$WORKING ${SYNC_MIRROR_TEXT}...\n" + local commands=() case "${SYSTEM_FACTIONS}" in "${SYSTEM_DEBIAN}" | "${SYSTEM_OPENKYLIN}") - apt-get update + commands+=("apt-get update") ;; "${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}") local package_manager="$(get_package_manager)" - $package_manager makecache + commands+=("${package_manager} makecache") ;; "${SYSTEM_OPENSUSE}") - zypper ref + commands+=("zypper ref") ;; "${SYSTEM_ARCH}") - pacman -Sy + commands+=("pacman -Sy") ;; "${SYSTEM_ALPINE}") - apk update -f + commands+=("apk update -f") ;; "${SYSTEM_GENTOO}") - emerge --sync --quiet + commands+=("emerge --sync --quiet") ;; "${SYSTEM_NIXOS}") - nix-store --verify - nix-channel --update + commands+=("nix-store --verify") + commands+=("nix-channel --update") ;; esac - if [ $? -eq 0 ]; then - echo -e "\n$SUCCESS 软件源更换完毕" + 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}" + if [ $? -ne 0 ]; then + echo '' + exit 1 + fi else - echo -e "\n$FAIL 软件源更换完毕,但${SYNC_MIRROR_TEXT}失败\n" - echo -e "请再次执行脚本并更换相同软件源后进行尝试,若仍然${SYNC_MIRROR_TEXT}失败那么可能由以下原因导致:\n" - echo -e "1. 网络连通性问题:例如连接异常、由地区影响的网络间歇式中断、禁止外部访问、软件源网站防火墙阻断等\n" - echo -e "2. 目标软件源异常:请手动前往软件源(镜像站)地址进行验证:${WEB_PROTOCOL}://${SOURCE}/${SOURCE_BRANCH}\n" - echo -e " 若报错内容是提示某个文件不存在那么有可能是软件源的问题,多常见于正在同步中的软件源仓库" - echo -e " 若报错内容是目录(path)不存在也有可能是目标软件源不存在当前系统镜像仓库,即不支持当前系统" - echo -e " 建议更换其它镜像站进行尝试,少数情况下软件源若处于同步中状态则可能会出现文件同步错误问题\n" - echo -e "3. 原有软件源报错:请先排除系统原有的其它软件源报错,因为脚本不会干预这些无关的软件源配置,解决后重新运行脚本即可\n" - exit 1 + echo -e "\n$WORKING ${SYNC_MIRROR_TEXT}...\n" + for cmd in "${commands[@]}"; do + eval "${cmd}" + done + if [ $? -eq 0 ]; then + echo -e "\n$SUCCESS 软件源更换完毕" + else + echo -e "\n$FAIL 软件源更换完毕,但${SYNC_MIRROR_TEXT}失败\n" + echo -e "请再次执行脚本并更换相同软件源后进行尝试,若仍然${SYNC_MIRROR_TEXT}失败那么可能由以下原因导致:\n" + echo -e "1. 网络连通性问题:例如连接异常、由地区影响的网络间歇式中断、禁止外部访问、软件源网站防火墙阻断等\n" + echo -e "2. 目标软件源异常:请手动前往软件源(镜像站)地址进行验证:${WEB_PROTOCOL}://${SOURCE}/${SOURCE_BRANCH}\n" + echo -e " 若报错内容是提示某个文件不存在那么有可能是软件源的问题,多常见于正在同步中的软件源仓库" + echo -e " 若报错内容是目录(path)不存在也有可能是目标软件源不存在当前系统镜像仓库,即不支持当前系统" + echo -e " 建议更换其它镜像站进行尝试,少数情况下软件源若处于同步中状态则可能会出现文件同步错误问题\n" + echo -e "3. 原有软件源报错:请先排除系统原有的其它软件源报错,因为脚本不会干预这些无关的软件源配置,解决后重新运行脚本即可\n" + exit 1 + fi fi } @@ -1731,29 +1766,45 @@ function upgrade_software() { esac fi fi - - echo -e '' + local commands=() case "${SYSTEM_FACTIONS}" in "${SYSTEM_DEBIAN}" | "${SYSTEM_OPENKYLIN}") - apt-get upgrade -y + commands+=("apt-get upgrade -y") ;; "${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}") local package_manager="$(get_package_manager)" - $package_manager upgrade -y --skip-broken + commands+=("${package_manager} upgrade -y --skip-broken") ;; "${SYSTEM_OPENSUSE}") - zypper update -y + commands+=("zypper update -y") ;; "${SYSTEM_ALPINE}") - apk upgrade --no-cache + commands+=("apk upgrade --no-cache") ;; "${SYSTEM_GENTOO}") - emerge --update --deep --with-bdeps=y --ask=n @world + commands+=("emerge --update --deep --with-bdeps=y --ask=n @world") ;; "${SYSTEM_NIXOS}") - nixos-rebuild switch + commands+=("nixos-rebuild switch") ;; 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}" "更新软件包" + else + echo '' + for cmd in "${commands[@]}"; do + eval "${cmd}" + done + fi if [[ "${CLEAN_CACHE}" == "false" ]]; then return fi @@ -2360,7 +2411,7 @@ function change_mirrors_ArchLinux() { ## 使用官方源 if [[ "${USE_OFFICIAL_SOURCE}" == "true" ]]; then SOURCE="mirrors.aliyun.com" - echo -e "\n${TIP} 由于 Arch Linux 无官方源因此已切换至阿里源\n" + [[ "${PURE_MODE}" != "true" ]] && echo -e "\n${TIP} 由于 Arch Linux 无官方源因此已切换至阿里源\n" fi ## 修改源 case "${SOURCE_BRANCH}" in @@ -2483,8 +2534,8 @@ function change_mirrors_or_install_EPEL() { ## 跳过较旧的 EOF 版本(epel 7 已被官方移动至 archive 仓库,目前没有多少镜像站同步,暂无适配的必要) if [[ "${epel_version}" == "7" ]]; then [ -z "${SOURCE_EPEL_BRANCH}" ] && SOURCE_EPEL_BRANCH="epel-archive" - echo -e "\n$WARN Extra Packages for Enterprise Linux 7 已结束生命周期并被官方移至归档库!" - echo -e "\n$TIP 目前部分镜像站没有同步该归档仓库,若换源后出现错误那么请先检查目标镜像站是否支持该仓库。\n\n${GREEN}➜${PLAIN} ${WEB_PROTOCOL}://${SOURCE_EPEL:-"${SOURCE}"}/${SOURCE_EPEL_BRANCH:-"epel"}" + [[ "${PURE_MODE}" != "true" ]] && echo -e "\n$WARN Extra Packages for Enterprise Linux 7 已结束生命周期并被官方移至归档库!" + [[ "${PURE_MODE}" != "true" ]] && echo -e "\n$TIP 目前部分镜像站没有同步该归档仓库,若换源后出现错误那么请先检查目标镜像站是否支持该仓库。\n\n${GREEN}➜${PLAIN} ${WEB_PROTOCOL}://${SOURCE_EPEL:-"${SOURCE}"}/${SOURCE_EPEL_BRANCH:-"epel"}" fi ## 安装 EPEL 软件包 if [ $VERIFICATION_EPEL -ne 0 ]; then @@ -2734,6 +2785,206 @@ function interactive_select_boolean() { tput cnorm 2>/dev/null # 恢复光标 } +function animate_exec() { + local cmd="$1" + local title="$2" + local max_lines=${3:-5} # 默认显示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)) + # 快速判断:如果ASCII行长度在范围内,直接返回 + if [[ "${line}" =~ ^[[:ascii:]]*$ && ${#line} -le $display_width ]]; then + echo "${line}" + return + fi + # 1. 计算非ASCII字符数量 + local non_ascii_count=$(echo "${line// /}" | sed "s|[0-9a-zA-Z\.\=\:\_\(\)\'\"-\/\!·]||g;" | wc -m) + # 2. 总字符数 + local total_length=${#line} + # 3. 实际显示宽度 = 总字符数 + 非ASCII字符数 + # 非ASCII字符额外占用1个宽度单位(即总共2个) + local display_length=$((total_length + non_ascii_count)) + # 4. 中文引号特殊处理(引号只占1个宽度,需要减去额外计算的部分) + local quote_count=0 + [[ $(echo "${line}" | grep -c "“") -gt 0 ]] && quote_count=$((quote_count + $(echo "${line}" | grep -c "“"))) + [[ $(echo "${line}" | grep -c "”") -gt 0 ]] && quote_count=$((quote_count + $(echo "${line}" | grep -c "”"))) + [[ $(echo "${line}" | grep -c "‘") -gt 0 ]] && quote_count=$((quote_count + $(echo "${line}" | grep -c "‘"))) + [[ $(echo "${line}" | grep -c "’") -gt 0 ]] && quote_count=$((quote_count + $(echo "${line}" | grep -c "’"))) + # 5. 调整宽度(减去引号额外计算的部分) + 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 + # 是否是中文等宽字符(非ASCII) + if ! [[ "$char" =~ [0-9a-zA-Z\.\=\:\_\(\)\'\"-\/\!·] ]]; then + # 不是中文引号则算2个宽度 + 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 提示 \033[0m \033[31m操作已取消\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 +} + ############################################################################## ## 生成 CentOS repo 源文件 diff --git a/DockerInstallation.sh b/DockerInstallation.sh index 526341b..d3ff193 100755 --- a/DockerInstallation.sh +++ b/DockerInstallation.sh @@ -1,6 +1,6 @@ #!/bin/bash ## Author: SuperManito -## Modified: 2025-04-01 +## Modified: 2025-04-05 ## License: MIT ## GitHub: https://github.com/SuperManito/LinuxMirrors ## Website: https://linuxmirrors.cn @@ -158,6 +158,7 @@ function handle_command_options() { --close-firewall 是否关闭防火墙 true 或 false --clean-screen 是否在运行前清除屏幕上的所有内容 true 或 false --ignore-backup-tips 忽略覆盖备份提示 无 + --pure-mode 纯净模式,精简打印内容 无 问题报告 https://github.com/SuperManito/LinuxMirrors/issues " @@ -294,6 +295,10 @@ function handle_command_options() { output_error "命令选项 ${BLUE}$1${PLAIN} 无效,请在该选项后指定 true 或 false !" fi ;; + ## 纯净模式 + --pure-mode) + PURE_MODE="true" + ;; ## 命令帮助 --help) output_command_help @@ -305,11 +310,12 @@ function handle_command_options() { 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() { @@ -318,15 +324,22 @@ function run_start() { elif [ "${CLEAN_SCREEN}" == "true" ]; then clear fi - echo -e '+-----------------------------------+' + if [[ "${PURE_MODE}" == "true" ]]; then + return + fi + 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 '欢迎使用 Docker Engine 安装与换源脚本' + echo -e "+-----------------------------------+" + echo -e "欢迎使用 Docker Engine 安装与换源脚本" } ## 运行结束 function run_end() { + if [[ "${PURE_MODE}" == "true" ]]; then + echo '' + return + fi echo -e "\n✨ 脚本运行完毕,更多使用教程详见官网 👉 \033[3mhttps://linuxmirrors.cn\033[0m\n\n🔥 1Panel · Linux 面板|极简运维 ➜ https://1panel.cn \033[3;2m【广告】\033[0m\n🔥 林枫云 · 专注独立IP高频VPS|R9/i9系列定制 ➜ https://www.dkdun.cn \033[3;2m【广告】\033[0m\n\n\033[3;1mPowered by \033[34mLinuxMirrors\033[0m\n" } @@ -569,7 +582,7 @@ function choose_mirrors() { echo -e "系统时间 ${BLUE}${date_time} ${timezone}${PLAIN}" } - print_title + [[ "${PURE_MODE}" != "true" ]] && print_title local mirror_list_name if [[ -z "${SOURCE}" ]]; then @@ -707,7 +720,7 @@ function close_firewall_service() { ## 安装环境包 function install_dependency_packages() { - local package_manager + local commands package_manager ## 删除原有源 case "${SYSTEM_FACTIONS}" in "${SYSTEM_DEBIAN}") @@ -718,39 +731,73 @@ function install_dependency_packages() { rm -rf $Dir_YumRepos/*docker*.repo ;; esac - echo -e "\n$WORKING ${SYNC_MIRROR_TEXT}...\n" + ## 更新软件源 + commands=() case "${SYSTEM_FACTIONS}" in "${SYSTEM_DEBIAN}") package_manager="apt-get" - $package_manager update + commands+=("${package_manager} update") ;; "${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}") package_manager="$(get_package_manager)" - $package_manager makecache + commands+=("${package_manager} makecache") ;; esac - VERIFICATION_SOURCESYNC=$? - if [ ${VERIFICATION_SOURCESYNC} -ne 0 ]; then + 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 -e "\n$WORKING ${SYNC_MIRROR_TEXT}...\n" + for cmd in "${commands[@]}"; do + eval "${cmd}" + done + echo -e "\n$COMPLETE ${SYNC_MIRROR_TEXT}结束\n" + fi + if [ $? -ne 0 ]; then output_error "${SYNC_MIRROR_TEXT}出错,请先解决系统原有软件源错误以确保 ${BLUE}${package_manager}${PLAIN} 软件包管理工具可用!" fi - echo -e "\n$COMPLETE ${SYNC_MIRROR_TEXT}结束\n" + + commands=() case "${SYSTEM_FACTIONS}" in "${SYSTEM_DEBIAN}") - $package_manager install -y apt-transport-https ca-certificates curl gnupg-agent software-properties-common + commands+=("${package_manager} install -y ca-certificates curl") ;; "${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}") - # 注:红帽 8 版本才发布了 dnf 包管理工具,为了兼容性而优先选择安装 dnf-utils + # 注:红帽 8 版本才发布了 dnf 包管理工具 case "${SYSTEM_VERSION_ID_MAJOR}" in 7) - $package_manager install -y yum-utils device-mapper-persistent-data lvm2 + commands+=("${package_manager} install -y yum-utils device-mapper-persistent-data lvm2") ;; *) - $package_manager install -y dnf-utils device-mapper-persistent-data lvm2 + commands+=("${package_manager} install -y dnf-plugins-core") ;; esac ;; 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 + echo '' + animate_exec "${exec_cmd}" "安装环境软件包" + else + for cmd in "${commands[@]}"; do + eval "${cmd}" + done + fi } ## 选择系统包管理器 @@ -804,6 +851,7 @@ function uninstall_original_version() { ## 配置 Docker CE 源 function configure_docker_ce_mirror() { + local commands=() case "${SYSTEM_FACTIONS}" in "${SYSTEM_DEBIAN}") ## 处理 GPG 密钥 @@ -818,10 +866,17 @@ function configure_docker_ce_mirror() { chmod a+r $file_keyring ## 添加源 echo "deb [arch=$(dpkg --print-architecture) signed-by=${file_keyring}] ${WEB_PROTOCOL}://${SOURCE}/linux/${SOURCE_BRANCH} ${SYSTEM_VERSION_CODENAME} stable" | tee $Dir_DebianExtendSource/docker.list >/dev/null 2>&1 - apt-get update + commands+=("apt-get update") ;; "${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}") - yum-config-manager -y --add-repo https://${SOURCE}/linux/${SOURCE_BRANCH}/docker-ce.repo + case "${SYSTEM_VERSION_ID_MAJOR}" in + 7) + yum-config-manager -y --add-repo https://${SOURCE}/linux/${SOURCE_BRANCH}/docker-ce.repo + ;; + *) + dnf config-manager -y --add-repo https://${SOURCE}/linux/${SOURCE_BRANCH}/docker-ce.repo + ;; + esac sed -i "s|https://download.docker.com|${WEB_PROTOCOL}://${SOURCE}|g" $Dir_YumRepos/docker-ce.repo ## 兼容处理版本号 if [[ "${SYSTEM_JUDGMENT}" != "${SYSTEM_FEDORA}" ]]; then @@ -837,10 +892,26 @@ function configure_docker_ce_mirror() { esac sed -i "s|\$releasever|${target_version}|g" $Dir_YumRepos/docker-ce.repo local package_manager="$(get_package_manager)" - $package_manager makecache + commands+=("${package_manager} makecache") fi ;; 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}" "${SYNC_MIRROR_TEXT}" + else + for cmd in "${commands[@]}"; do + eval "${cmd}" + done + fi } ## 安装 Docker Engine @@ -866,14 +937,15 @@ function install_docker_engine() { ## 安装 function install_main() { local target_docker_version + local commands=() if [[ "${INSTALL_LATESTED_DOCKER}" == "true" ]]; then case "${SYSTEM_FACTIONS}" in "${SYSTEM_DEBIAN}") - apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin + commands+=("apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin") ;; "${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}") local package_manager="$(get_package_manager)" - $package_manager install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin + commands+=("${package_manager} install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin") ;; esac else @@ -936,14 +1008,31 @@ function install_docker_engine() { INSTALL_JUDGMENT="5:" ;; esac - apt-get install -y docker-ce=${INSTALL_JUDGMENT}${target_docker_version}* docker-ce-cli=5:${target_docker_version}* containerd.io docker-buildx-plugin docker-compose-plugin + commands+=("apt-get install -y docker-ce=${INSTALL_JUDGMENT}${target_docker_version}* docker-ce-cli=5:${target_docker_version}* containerd.io docker-buildx-plugin docker-compose-plugin") ;; "${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}") local package_manager="$(get_package_manager)" - $package_manager install -y docker-ce-${target_docker_version} docker-ce-cli-${target_docker_version} containerd.io docker-buildx-plugin docker-compose-plugin + commands+=("${package_manager} install -y docker-ce-${target_docker_version} docker-ce-cli-${target_docker_version} containerd.io docker-buildx-plugin docker-compose-plugin") ;; esac fi + 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}" "安装 Docker Engine" + else + for cmd in "${commands[@]}"; do + eval "${cmd}" + done + fi + [ $? -ne 0 ] && output_error "安装 Docker Engine 失败!" } ## 修改 Docker Registry 镜像仓库源 @@ -1022,7 +1111,6 @@ function install_docker_engine() { ;; esac fi - echo '' fi ## 判定是否已安装 @@ -1047,7 +1135,6 @@ function install_docker_engine() { fi uninstall_original_version install_main - [ $? -ne 0 ] && output_error "安装 Docker Engine 失败!" change_docker_registry_mirror } @@ -1069,8 +1156,8 @@ function check_version() { ;; "${SYSTEM_REDHAT}" | "${SYSTEM_OPENEULER}" | "${SYSTEM_OPENCLOUDOS}" | "${SYSTEM_ANOLISOS}") local package_manager="$(get_package_manager)" - echo -e "\n检查源文件:cat $Dir_YumRepos/docker.repo" - echo -e "请尝试手动执行安装命令:$package_manager install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin\n" + echo -e "\n检查源文件:cat ${Dir_YumRepos}/docker.repo" + echo -e "请尝试手动执行安装命令:${package_manager} install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin\n" ;; esac exit 1 @@ -1268,5 +1355,205 @@ function interactive_select_boolean() { tput cnorm 2>/dev/null # 恢复光标 } +function animate_exec() { + local cmd="$1" + local title="$2" + local max_lines=${3:-5} # 默认显示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)) + # 快速判断:如果ASCII行长度在范围内,直接返回 + if [[ "${line}" =~ ^[[:ascii:]]*$ && ${#line} -le $display_width ]]; then + echo "${line}" + return + fi + # 1. 计算非ASCII字符数量 + local non_ascii_count=$(echo "${line// /}" | sed "s|[0-9a-zA-Z\.\=\:\_\(\)\'\"-\/\!·]||g;" | wc -m) + # 2. 总字符数 + local total_length=${#line} + # 3. 实际显示宽度 = 总字符数 + 非ASCII字符数 + # 非ASCII字符额外占用1个宽度单位(即总共2个) + local display_length=$((total_length + non_ascii_count)) + # 4. 中文引号特殊处理(引号只占1个宽度,需要减去额外计算的部分) + local quote_count=0 + [[ $(echo "${line}" | grep -c "“") -gt 0 ]] && quote_count=$((quote_count + $(echo "${line}" | grep -c "“"))) + [[ $(echo "${line}" | grep -c "”") -gt 0 ]] && quote_count=$((quote_count + $(echo "${line}" | grep -c "”"))) + [[ $(echo "${line}" | grep -c "‘") -gt 0 ]] && quote_count=$((quote_count + $(echo "${line}" | grep -c "‘"))) + [[ $(echo "${line}" | grep -c "’") -gt 0 ]] && quote_count=$((quote_count + $(echo "${line}" | grep -c "’"))) + # 5. 调整宽度(减去引号额外计算的部分) + 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 + # 是否是中文等宽字符(非ASCII) + if ! [[ "$char" =~ [0-9a-zA-Z\.\=\:\_\(\)\'\"-\/\!·] ]]; then + # 不是中文引号则算2个宽度 + 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 提示 \033[0m \033[31m操作已取消\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 +} + handle_command_options "$@" main diff --git a/docs/other/index.md b/docs/other/index.md index 53951a2..004644d 100644 --- a/docs/other/index.md +++ b/docs/other/index.md @@ -7,6 +7,8 @@ hide: > 如果觉得这个项目不错对您有所帮助的话,请点击仓库右上角的 Star 并分享给更多的朋友 :octicons-heart-fill-24:{ .heart style="color: red" } +!!! tip inline end "本项目脚本已被众多开源项目使用,广受社区用户好评" + ## :simple-docker:{style="color: #1d63ed"} Docker 安装脚本 @@ -101,6 +103,7 @@ hide: | `--close-firewall` | 是否关闭防火墙 | `true` 或 `false` | | `--clean-screen` | 是否在运行前清除屏幕上的所有内容 | `true` 或 `false` | | `--ignore-backup-tips` | 忽略覆盖备份提示(即不覆盖备份) | 无 | + | `--pure-mode` | 纯净模式,精简打印内容 | 无 | > 软件源完整格式 `://<软件源地址(域名或IP)>/<软件源仓库(路径)>` @@ -120,6 +123,7 @@ hide: --close-firewall 是否关闭防火墙 true 或 false --clean-screen 是否在运行前清除屏幕上的所有内容 true 或 false --ignore-backup-tips 忽略覆盖备份提示 无 + --pure-mode 纯净模式,精简打印内容 无 问题报告 https://github.com/SuperManito/LinuxMirrors/issues ``` @@ -156,7 +160,7 @@ hide: ``` > 如果指定的版本不存在或者不支持当前系统,届时脚本会报错跳出 - ??? tip "如何查看版本列表?" + ??? quote "查看版本列表的方法" === "Debian 系" @@ -169,7 +173,7 @@ hide: === "RedHat 系 / openEuler / OpenCloudOS / Anolis OS" ``` bash - yum list docker-ce --showduplicates | sort -r | awk '{print $2}' | grep -Eo "[0-9][0-9].[0-9]{1,2}.[0-9]{1,2}" | sort -t '.' -k1,1nr -k2,2nr -k3,3nr + dnf list docker-ce --showduplicates | sort -r | awk '{print $2}' | grep -Eo "[0-9][0-9].[0-9]{1,2}.[0-9]{1,2}" | sort -t '.' -k1,1nr -k2,2nr -k3,3nr ``` > `Red Hat Enterprise Linux`   `CentOS`   `Rocky Linux`   `AlmaLinux`   `Fedora`   `openEuler`   `OpenCloudOS`   `Anolis OS` @@ -183,7 +187,7 @@ hide: ``` { .bash .no-copy title="参考命令" } bash <(curl -sSL https://linuxmirrors.cn/docker.sh) \ - --source mirror.example.com \ + --source mirror.example.com/docker-ce \ --source-registry registry.hub.docker.com \ --protocol http \ --use-intranet-source false \ @@ -192,6 +196,15 @@ hide: --ignore-backup-tips ``` + - #### 纯净模式 + + !!! tip "该功能目前处于试验阶段,滚动输出的命令日志可能存在无法预料的显示问题,目前暂未发现异常" + + ``` bash + bash <(curl -sSL https://linuxmirrors.cn/docker.sh) --pure-mode + ``` + > 为了便于开发者使用故推出此功能,启用后会精简脚本内容输出,建议搭配其它命令选项无交互使用 + - ### 关于服务报错无法启动 !!! quote "" diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css index e8e492c..ccdb4d8 100644 --- a/docs/stylesheets/extra.css +++ b/docs/stylesheets/extra.css @@ -33,6 +33,10 @@ content: url(/assets/images/brand/svg/logo-dark.svg); } +[data-md-color-scheme="slate"] .md-typeset .admonition.quote, .md-typeset details.quote { + border-color: hsla(225deg,15%,90%,0.24); +} + @font-face { font-family: 'SF Mono'; src: url('./fonts/SF-Mono-Regular.otf'); diff --git a/docs/use/index.md b/docs/use/index.md index 2dc5d0d..ddbaefb 100644 --- a/docs/use/index.md +++ b/docs/use/index.md @@ -240,36 +240,11 @@ hide: 如果以上方法试了都不行,那就复制[源码](https://gitee.com/SuperManito/LinuxMirrors/raw/main/ChangeMirrors.sh)至本地新建任意名称的 `.sh` 脚本,粘贴源码内容后通过 `bash` 指令手动执行。 - - #### 关于开启 SSH 远程登录的方法 + - #### 备份原有软件源 !!! quote "" - - 验证是否已安装 `SSH` 服务 - - ``` bash - ls /etc | grep ssh - ``` - > 如果没有这个文件夹说明系统未安装 `SSH` 服务,你需要通过包管理工具安装 `openssh` 软件包 - - - 设置允许 Root 用户登录 - - ``` bash - cat /etc/ssh/sshd_config | grep -Eq "^[# ]?PermitRootLogin " ; [ $? -eq 0 ] && sed -i 's/^[# ]\?PermitRootLogin.*/PermitRootLogin yes/g' /etc/ssh/sshd_config || echo -e "\nPermitRootLogin yes" >> /etc/ssh/sshd_config - ``` - - - 设置密码认证 - - ``` bash - cat /etc/ssh/sshd_config | grep -Eq "^[# ]?PasswordAuthentication " ; [ $? -eq 0 ] && sed -i 's/^[# ]\?PasswordAuthentication.*/PasswordAuthentication yes/g' /etc/ssh/sshd_config || echo -e "\nPasswordAuthentication yes" >> /etc/ssh/sshd_config - ``` - - - 启动/重启 `SSH` 服务 - - ``` bash - ps -ef | grep -q ssh ; [ $? -eq 0 ] && systemctl restart sshd || systemctl enable --now sshd - ``` - - > 命令以及配置步骤仅供参考,只适配了部分常见发行版 + 脚本会自动备份原有软件源内容,备份路径为原有文件或目录的绝对路径加上 `.bak` 后缀,例如 `/etc/apt/sources.list => /etc/apt/sources.list.bak`,当检查到已存在备份内容时会询问是否覆盖备份。 - #### 还原已备份的软件源 @@ -328,6 +303,77 @@ hide: cp -rf /etc/nix/nix.conf.bak /etc/nix/nix.conf ``` + - #### 关于开启 SSH 远程登录的方法 + + !!! quote "" + + - 验证是否已安装 `SSH` 服务 + + ``` bash + ls /etc | grep ssh + ``` + > 如果没有这个文件夹说明系统未安装 `SSH` 服务,你需要通过包管理工具安装 `openssh` 软件包 + + - 设置允许 Root 用户登录 + + ``` bash + cat /etc/ssh/sshd_config | grep -Eq "^[# ]?PermitRootLogin " ; [ $? -eq 0 ] && sed -i 's/^[# ]\?PermitRootLogin.*/PermitRootLogin yes/g' /etc/ssh/sshd_config || echo -e "\nPermitRootLogin yes" >> /etc/ssh/sshd_config + ``` + + - 设置密码认证 + + ``` bash + cat /etc/ssh/sshd_config | grep -Eq "^[# ]?PasswordAuthentication " ; [ $? -eq 0 ] && sed -i 's/^[# ]\?PasswordAuthentication.*/PasswordAuthentication yes/g' /etc/ssh/sshd_config || echo -e "\nPasswordAuthentication yes" >> /etc/ssh/sshd_config + ``` + + - 启动/重启 `SSH` 服务 + + ``` bash + ps -ef | grep -q ssh ; [ $? -eq 0 ] && systemctl restart sshd || systemctl enable --now sshd + ``` + + > 命令以及配置步骤仅供参考,只适配了部分常见发行版 + + - #### 关于调用脚本的互联网位置 + + !!! quote "" + + 项目利用 [GitHub Action](https://github.com/SuperManito/LinuxMirrors/blob/main/.github/workflows/build-docs.yml#L29) 在每次提交后自动拷贝源码到文档目录作为网站资源发布,网站托管于 :netlify: [Netlify](https://www.netlify.com),几乎没有被劫持的风险请放心使用。 + + 当然你也可以使用代码托管仓库的原始地址来调用,这里只是想告诉你为什么会有几个不同的地址,默认的官网地址更易于记忆和访问。 + + - #### 关于软件源下载速度相关问题 + + !!! quote "" + + 首先,在[软件源列表](../mirrors/index.md)的使用帮助处有写使用推荐,这是根据以往经验总结出来的,但总有用户在纠结软件源速度的问题。 + + 软件源(镜像站)的网络延迟即 `Ping` 与下载速度没有太大的关联,双方地理位置间隔的远近不代表实际体验,有些镜像站下行带宽很高但实际测速却并不理想,因为这与镜像站的并发性能和负载策略有关。 + + 网上也有很多基于 C、Python 编写的镜像站测速开源脚本,而本项目脚本基于 Bash Shell 编写且不依赖任何第三方库,Bash 是 Linux 运维中最常用的脚本语言并且绝大部分发行版都会预装,这意味着用户不需要安装任何环境就能直接运行,这种便利性是其它高级语言无法替代的,不过目前来看 Bash 脚本可能无法实现精准测速的功能,使用其它高级语言编写测速功能无疑是造轮子的行为。 + + - #### 关于未启用的软件源仓库 + + !!! quote "" + + 很多系统的软件源会启用多个仓库,脚本遵循系统默认设置,默认不启用的软件源(仓库)不会在运行完本脚本后被启用,但是它们也随脚本更换了目标软件源地址,具体启用方法如下: + + === "Debian 系 / openKylin" + + 默认禁用了`deb-src`源码仓库和`proposed`预发布软件源,若需启用请将 `/etc/apt/sources.list` 文件中相关内容的所在行取消注释 + + > `Debian`   `Ubuntu`   `Kali`   `Linux Mint`   `Deepin`   `Zorin OS`   `Armbian`   `Proxmox VE`   `Raspberry Pi OS`   `openKylin` + + === "RedHat 系 / openEuler / OpenCloudOS / Anolis OS" + + 部分仓库默认没有启用,若需启用请将 `/etc/yum.repos.d` 目录下相关 repo 文件中的 `enabled` 值修改为 `1` + + > `Red Hat Enterprise Linux`   `CentOS`   `Rocky Linux`   `AlmaLinux`   `Fedora`   `openEuler`   `OpenCloudOS`   `Anolis OS` + + === "openSUSE" + + 部分仓库默认没有启用,若需启用请将 `/etc/zypp/repos.d` 目录下相关 repo 文件中的 `enabled` 值修改为 `1` + - #### 其它 !!! quote "" @@ -335,44 +381,6 @@ hide: - 如果提示 `bash: /proc/self/fd/11: No such file or directory`,请切换至 `Root` 用户执行,切换命令为 `sudo -i` 或 `su root` - 如果交互打印界面发现是输入而不是新式的方向键交互,那么请自行安装 `ncurses` 或 `nano` 软件包,新式的方向键交互依赖 `tput` 指令实现。 -- ### 关于备份原有软件源 - - 脚本会自动备份原有软件源内容,备份路径为原有文件或目录的绝对路径加上 `.bak` 后缀,例如 `/etc/apt/sources.list => /etc/apt/sources.list.bak`,当检查到已存在备份内容时会询问是否覆盖备份。 - -- ### 关于调用脚本的互联网位置 - - 项目利用 [GitHub Action](https://github.com/SuperManito/LinuxMirrors/blob/main/.github/workflows/build-docs.yml#L29) 在每次提交后自动拷贝源码到文档目录作为网站资源发布,网站托管于 :netlify: Netlify,几乎没有被劫持的风险请放心使用。 - - 当然你也可以使用代码托管仓库的原始地址来调用,这里只是想告诉你为什么会有几个不同的地址,默认的官网地址更易于记忆和访问。 - -- ### 关于软件源下载速度相关问题 - - 首先,在[软件源列表](../mirrors/index.md)的使用帮助处有写使用推荐,这是根据以往经验总结出来的,但总有用户在纠结软件源速度的问题。 - - 软件源(镜像站)的网络延迟即 `Ping` 与下载速度没有太大的关联,双方地理位置间隔的远近不代表实际体验,有些镜像站下行带宽很高但实际测速却并不理想,因为这与镜像站的并发性能和负载策略有关。 - - 网上也有很多基于 C、Python 编写的镜像站测速开源脚本,而本项目脚本基于 Bash Shell 编写且不依赖任何第三方库,Bash 是 Linux 运维中最常用的脚本语言并且绝大部分发行版都会预装,这意味着用户不需要安装任何环境就能直接运行,这种便利性是其它高级语言无法替代的,不过目前来看 Bash 脚本可能无法实现精准测速的功能,使用其它高级语言编写测速功能无疑是造轮子的行为。 - -- ### 关于未启用的软件源和仓库 - - 脚本遵循系统默认设置,默认不启用的软件源(仓库)不会在运行完本脚本后被启用,但是它们也随脚本更换了目标软件源地址,具体启用方法如下: - - === "Debian 系 / openKylin" - - 默认禁用了`deb-src`源码仓库和`proposed`预发布软件源,若需启用请将 `/etc/apt/sources.list` 文件中相关内容的所在行取消注释 - - > `Debian`   `Ubuntu`   `Kali`   `Linux Mint`   `Deepin`   `Zorin OS`   `Armbian`   `Proxmox VE`   `Raspberry Pi OS`   `openKylin` - - === "RedHat 系 / openEuler / OpenCloudOS / Anolis OS" - - 部分仓库默认没有启用,若需启用请将 `/etc/yum.repos.d` 目录下相关 repo 文件中的 `enabled` 值修改为 `1` - - > `Red Hat Enterprise Linux`   `CentOS`   `Rocky Linux`   `AlmaLinux`   `Fedora`   `openEuler`   `OpenCloudOS`   `Anolis OS` - - === "openSUSE" - - 部分仓库默认没有启用,若需启用请将 `/etc/zypp/repos.d` 目录下相关 repo 文件中的 `enabled` 值修改为 `1` - --- @@ -401,11 +409,12 @@ hide: | `--install-epel` | 是否安装 EPEL 附加软件包 | `true` 或 `false` | | `--backup` | 是否备份原有软件源 | `true` 或 `false` | | `--upgrade-software` | 是否更新软件包 | `true` 或 `false` | -| `--clean-cache` | 是否清理下载缓存 | `true` 或 `false` | +| `--clean-cache` | 是否在更新软件包后清理下载缓存 | `true` 或 `false` | | `--clean-screen` | 是否在运行前清除屏幕上的所有内容 | `true` 或 `false` | | `--print-diff` | 是否打印源文件修改前后差异 | `true` 或 `false` | | `--only-epel` | 仅更换 EPEL 软件源模式 | 无 | | `--ignore-backup-tips` | 忽略覆盖备份提示(即不覆盖备份) | 无 | +| `--pure-mode` | 纯净模式,精简打印内容 | 无 | | `--help` | 查看帮助菜单 | 无 | > 软件源完整格式 `://<软件源地址(域名或IP)>/<软件源仓库(路径)>` @@ -437,11 +446,12 @@ $ bash ChangeMirrors.sh --help --install-epel 是否安装 EPEL 附加软件包 true 或 false --backup 是否备份原有软件源 true 或 false --upgrade-software 是否更新软件包 true 或 false - --clean-cache 是否清理下载缓存 true 或 false + --clean-cache 是否在更新软件包后清理下载缓存 true 或 false --clean-screen 是否在运行前清除屏幕上的所有内容 true 或 false --only-epel 仅更换 EPEL 软件源模式 无 --ignore-backup-tips 忽略覆盖备份提示 无 --print-diff 打印源文件修改前后差异 无 + --pure-mode 纯净模式,精简打印内容 无 问题报告 https://github.com/SuperManito/LinuxMirrors/issues ``` @@ -456,9 +466,9 @@ $ bash ChangeMirrors.sh --help - ### 指定软件源仓库 - 主要使用场景:目标镜像站有对应的系统镜像仓库但是不符合本项目脚本关于软件源仓库设置的默认规则 + 这里的软件源仓库与系统内容软件源仓库不同,指的是软件源地址后面的路径即镜像站分支仓库,虽然名义上都是仓库但是非常容易混淆 - 由于仓库软件源仓库作用在软件源地址上,因此也可以使用多级路径,例如 `://<软件源地址>/linux/debian` > `https://mirrors.example.com/linux/debian`, + 主要使用场景:目标镜像站有对应的系统镜像仓库但是不符合本项目脚本关于软件源仓库设置的默认规则 ??? note "项目对于各操作系统所使用的默认仓库名称(点击展开查看)" @@ -490,7 +500,6 @@ $ bash ChangeMirrors.sh --help | Gentoo | `gentoo` `gentoo-portage` | | NixOS | `nix-channels` | - 请看下面的例子 ``` { .bash title="使用阿里云镜像站的 Rocky Linux 软件源" } @@ -503,11 +512,13 @@ $ bash ChangeMirrors.sh --help > 部分系统会同时配置多个仓库的软件源,具体详见命令选项 + > 由于软件源仓库作用在软件源地址上因此也可以使用多级路径,例如 `--branch "linux/debian"` -> `https://mirrors.example.com/linux/debian`, + - ### 单独更换 EPEL 源 !!! info "EPEL (Extra Packages for Enterprise Linux) 是由 Fedora 组织维护的一个附加软件包仓库,它主要适用于除 Fedora 操作系统以外的红帽系 Linux 发行版,配置 EPEL 仓库已成为广大用户的普遍需求,建议默认安装它" - 有些时候你会发现想使用的镜像站没有 epel 镜像仓库,那么你可以在第一次运行脚本时不安装或更换 epel 源然后再单独执行下面的命令 + 有些时候你会发现想使用的镜像站没有 EPEL 仓库,那么你可以在第一次运行脚本时不安装或更换 EPEL 源然后再单独执行下面的命令 ``` bash bash <(curl -sSL https://linuxmirrors.cn/main.sh) --only-epel @@ -582,6 +593,15 @@ $ bash ChangeMirrors.sh --help --ignore-backup-tips ``` +- ### 纯净模式 + + !!! tip "该功能目前处于试验阶段,滚动输出的命令日志可能存在无法预料的显示问题,目前暂未发现异常" + + ``` bash + bash <(curl -sSL https://linuxmirrors.cn/main.sh) --pure-mode + ``` + > 为了便于开发者使用故推出此功能,启用后会精简脚本内容输出,建议搭配其它命令选项无交互使用 + --- ## 定制脚本 @@ -621,8 +641,9 @@ $ bash ChangeMirrors.sh --help | `BACKUP` | 是否备份原有软件源 | `true` 或 `false` | | `IGNORE_BACKUP_TIPS` | 忽略覆盖备份提示(即不覆盖备份) | `true` 或 `false` | | `UPGRADE_SOFTWARE` | 是否更新软件包 | `true` 或 `false` | -| `CLEAN_CACHE` | 是否清理下载缓存 | `true` 或 `false` | +| `CLEAN_CACHE` | 是否在更新软件包后清理下载缓存 | `true` 或 `false` | | `CLEAN_SCREEN` | 是否在运行前清除屏幕上的所有内容 | `true` 或 `false` | | `PRINT_DIFF` | 是否打印源文件修改前后差异 | `true` 或 `false` | +| `PURE_MODE` | 纯净模式,精简打印内容 | `true` 或 `false` | > 部分变量存在默认值,未涉及的变量无需声明为空值(空字符串),另外如果对应功能配置不存在那么就可能会出现交互