#!/bin/bash ######### 自定义常量 ########## _constant() { script_version="v2024-04-25" old_IFS="$IFS" work_dir="./sp-github-i-abc" node_set="" node_set_1="https://raw.githubusercontent.com/i-abc/speedtest/node/all-node.txt" node_set_2="https://asset.bash.icu/https://raw.githubusercontent.com/i-abc/speedtest/node/all-node-mirror.txt" # url_1为官方源,url_2为镜像源,皆会进行SHA-256检测 # speedtest-cli,https://www.speedtest.net/zh-Hans/apps/cli speedtest_cli_version="1.2.0" speedtest_cli_tar_x86_64_sha256="5690596c54ff9bed63fa3732f818a05dbc2db19ad36ed68f21ca5f64d5cfeeb7" speedtest_cli_tar_i386_sha256="9ff7e18dbae7ee0e03c66108445a2fb6ceea6c86f66482e1392f55881b772fe8" speedtest_cli_tar_aarch64_sha256="3953d231da3783e2bf8904b6dd72767c5c6e533e163d3742fd0437affa431bd3" speedtest_cli_tar_armhf_sha256="e45fcdebbd8a185553535533dd032d6b10bc8c64eee4139b1147b9c09835d08d" speedtest_cli_tar_armel_sha256="629a455a2879224bd0dbd4b36d8c721dda540717937e4660b4d2c966029466bf" speedtest_cli_tar_url="" speedtest_cli_tar_url_1="https://install.speedtest.net/app/cli/ookla-speedtest-${speedtest_cli_version}-linux-${speedtest_cli_arch}.tgz" speedtest_cli_tar_url_2="https://asset.bash.icu/https://raw.githubusercontent.com/i-abc/speedtest/asset/speedtest-cli/v${speedtest_cli_version}/ookla-speedtest-${speedtest_cli_version}-linux-${speedtest_cli_arch}.tgz" # bim-core,https://github.com/veoco/bim-core bim_core_version="0.17.0" bim_core_tar_x86_64_sha256="109280fbf5a821bc56c2e83b906e65228ebc8436054b2e7861ca6db88f7e0330" bim_core_tar_aarch64_sha256="fa62357f94050fbb7851d1bbb7e393d8e1301281ce03c43b37dae55cbf08c198" bim_core_tar_url="" bim_core_tar_url_1="https://github.com/veoco/bim-core/releases/download/v${bim_core_version}/bimc-${bim_core_arch}-unknown-linux-musl" bim_core_tar_url_2="https://asset.bash.icu/https://github.com/veoco/bim-core/releases/download/v${bim_core_version}/bimc-${bim_core_arch}-unknown-linux-musl" # speedtest-go,https://github.com/showwin/speedtest-go speedtest_go_version="1.6.11" speedtest_go_tar_x86_64_sha256="2603cf54513dd770353d66095a30b13d170d225a0a60fc07ee6d5f5a78e15a27" speedtest_go_tar_i386_sha256="5ec1df89446aab656c0947c9028fa93b40ec83a4132e30b3dad978215103e294" speedtest_go_tar_arm64_sha256="68b1be4cd6dbbd3d68216a106b578fd2bdf51b8606d3597743aefdb2f0234c24" speedtest_go_tar_armv7_sha256="f30ba82ac4e6fd967302d505fa7380745d89a4ddcfb72f607b580ef507e5eb0a" speedtest_go_tar_armv6_sha256="ccdc0a4281c193cccc027e7c2fb2b961e1eb3e4eab1dcf68b46bd0dafd2e895f" speedtest_go_tar_url="" speedtest_go_tar_url_1="https://github.com/showwin/speedtest-go/releases/download/v${speedtest_go_version}/speedtest-go_${speedtest_go_version}_Linux_${speedtest_go_arch}.tar.gz" speedtest_go_tar_url_2="https://asset.bash.icu/https://github.com/showwin/speedtest-go/releases/download/v${speedtest_go_version}/speedtest-go_${speedtest_go_version}_Linux_${speedtest_go_arch}.tar.gz" # librespeed-cli,https://github.com/librespeed/speedtest-cli librespeed_cli_version="1.0.10" librespeed_cli_tar_amd64_sha256="8e6d020c17e11dba73f0eb8a11f7ae6e3d96cdb307faf3c0ec13aa54e0cba055" librespeed_cli_tar_386_sha256="0121bd4a21786d5964643b00a4193edfa15389050afe5579a290e67b706ef9eb" librespeed_cli_tar_arm64_sha256="0ecbb98abb39f17bde2c0efae23f8446f4596c3a824aa6dda9b71723386b03ed" librespeed_cli_tar_armv7_sha256="b121733b9a18aa646a16393396dd7fe59e8773420a38acb05b91652c4d6cb356" librespeed_cli_tar_armv6_sha256="def98bbf0e79805411bca312e9e6bcddd12cd0abf4e6584a3ae05aa20c762b64" librespeed_cli_tar_url="" librespeed_cli_tar_url_1="https://github.com/librespeed/speedtest-cli/releases/download/v${librespeed_cli_version}/librespeed-cli_${librespeed_cli_version}_linux_${librespeed_cli_arch}.tar.gz" librespeed_cli_tar_url_2="https://asset.bash.icu/https://github.com/librespeed/speedtest-cli/releases/download/v${librespeed_cli_version}/librespeed-cli_${librespeed_cli_version}_linux_${librespeed_cli_arch}.tar.gz" # 配色 red='\033[1;31m' green='\033[1;32m' yellow='\033[1;33m' blue='\033[1;34m' purple='\033[1;35m' cyan='\033[1;36m' endc='\033[0m' } ########## 进度条 ########### _show_progress_bar() { local bar="⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏" local i=0 local n="${#bar}" while sleep 0.1; do printf "\r ${green}测试进行中 %s${endc} " "${bar:i++%n:1}" done } ########## 横幅 ########### _print_banner_1() { echo "------------------------ 多功能 自更新 测速脚本 ------------------------" echo -e " Version : ${green}${script_version}${endc}" echo -e " Usage : ${yellow}bash <(curl -sL bash.icu/speedtest)${endc}" echo -e " GitHub : ${green}https://github.com/i-abc/speedtest${endc}" printf "%-72s\n" "-" | sed 's)\s)-)g' } _print_banner_2() { if [ -s "$work_dir"/banner-custom.txt ]; then cat "$work_dir"/banner-custom.txt printf "%-72s\n" "-" | sed 's)\s)-)g' fi } _print_banner_3() { printf "%-s%-s%-s%-s%-s\n" "测速节点 " "下载/Mbps " "上传/Mbps " "延迟/ms " "抖动/ms" } _print_banner_4() { printf "%-72s\n" "-" | sed 's)\s)-)g' echo "系统时间:$(date +"%Y-%m-%d %H:%M:%S %Z")" echo "北京时间: $(TZ=Asia/Shanghai date +"%Y-%m-%d %H:%M:%S %Z")" printf "%-72s\n\n" "-" | sed 's)\s)-)g' } ########## 检测是否为 root ########## _check_user() { sudo_flag="" if [[ "${UID}" -eq 0 ]]; then echo "当前用户是 root" else echo "当前用户不是 root" sudo_flag="sudo" fi } ########## 确认架构及其对应版本的程序 ########## _check_architecture() { speedtest_cli_arch="" bim_core_archh="" speedtest_go_arch="" librespeed_cli_arch="" local arch arch="$(uname -m)" if [ "$arch" == "x86_64" ]; then speedtest_cli_arch="x86_64" bim_core_arch="x86_64" speedtest_go_arch="x86_64" librespeed_cli_arch="amd64" elif [ "$arch" == "i386" ] || [ "$arch" == "i686" ]; then speedtest_cli_arch="i386" speedtest_go_arch="i386" librespeed_cli_arch="386" elif [ "$arch" == "armv8" ] || [ "$arch" == "armv8l" ] || [ "$arch" == "aarch64" ] || [ "$arch" == "arm64" ]; then speedtest_cli_arch="aarch64" bim_core_arch="aarch64" speedtest_go_arch="arm64" librespeed_cli_arch="arm64" elif [ "$arch" == "armv7" ] || [ "$arch" == "armv7l" ]; then speedtest_cli_arch="armhf" speedtest_go_arch="armv7" librespeed_cli_arch="armv7" elif [ "$arch" == "armv6" ]; then speedtest_cli_arch="armel" speedtest_go_arch="armv6" librespeed_cli_arch="armv6" fi } ########## 检测地区,指定下载源 ########## _check_region() { loc=$(curl -s -L "https://www.qualcomm.cn/cdn-cgi/trace" | awk -F'=' '/loc/{ print $2 }') echo "loc: $loc" if [ -z "$loc" ]; then echo "使用镜像源" node_set="$node_set_2" speedtest_cli_tar_url="$speedtest_cli_tar_url_2" bim_core_tar_url="$bim_core_tar_url_2" speedtest_go_tar_url="$speedtest_go_tar_url_2" librespeed_cli_tar_url="$librespeed_cli_tar_url_2" elif [ "$loc" != "CN" ]; then echo "使用默认源" node_set="$node_set_1" speedtest_cli_tar_url="$speedtest_cli_tar_url_1" bim_core_tar_url="$bim_core_tar_url_1" speedtest_go_tar_url="$speedtest_go_tar_url_1" librespeed_cli_tar_url="$librespeed_cli_tar_url_1" else echo "使用镜像源" node_set="$node_set_2" speedtest_cli_tar_url="$speedtest_cli_tar_url_2" bim_core_tar_url="$bim_core_tar_url_2" speedtest_go_tar_url="$speedtest_go_tar_url_2" librespeed_cli_tar_url="$librespeed_cli_tar_url_2" fi } ########## 检测某软件包是否安装,没安则自动安上,目前只支持RedHat、Debian系 ########## _check_package() { echo "正在检测所需的$1是否安装" # 检测软件包是否安装 if ! command -v "$1"; then # 确认包管理器并安装软件包 # RedHat if command -v dnf; then ${sudo_flag} dnf install -y "$2" elif command -v yum; then ${sudo_flag} yum install -y "$2" # Debian elif command -v apt; then ${sudo_flag} apt install -y "$2" # Arch Linux elif command -v pacman; then ${sudo_flag} pacman -S --noconfirm "$2" else echo "本机非RedHat、Debian、Arch Linux" echo "暂不支持自动安装所需的软件包" exit 1 fi # 再次检测软件包是否安装 if ! command -v "$1"; then echo "自动安装所需的$1失败" echo "请手动安装$1后再执行本脚本" exit 1 fi fi } ########## 创建工作目录 ########## _make_dir() { # 删除可能存在的残余文件 rm -rf "$work_dir" # 创建目录 mkdir "$work_dir" } ########## 下载程序 ########## _download_tar() { echo "$speedtest_cli_tar_url" echo "speedtest-cli下载中" curl --progress-bar -o "$work_dir"/speedtest-cli.tgz -L "$speedtest_cli_tar_url" echo "$bim_core_tar_url" echo "bim-core下载中" [ -n "$bim_core_arch" ] && curl --progress-bar -o "$work_dir"/bim-core -L "$bim_core_tar_url" echo "$speedtest_go_tar_url" echo "speedtest-go下载中" curl --progress-bar -o "$work_dir"/speedtest-go.tar.gz -L "$speedtest_go_tar_url" echo "$librespeed_cli_tar_url" echo "librespeed-cli下载中" curl --progress-bar -o "$work_dir"/librespeed-cli.tar.gz -L "$librespeed_cli_tar_url" } ########## 检测程序的SHA-256 ########## _check_tar_sha256() { local speedtest_cli_tar_download_sha256 bim_core_tar_download_sha256 speedtest_go_tar_download_sha256 librespeed_cli_tar_download_sha256 speedtest_cli_tar_download_sha256=$(sha256sum "$work_dir"/speedtest-cli.tgz | awk '{ print $1 }') bim_core_tar_download_sha256=$(sha256sum "$work_dir"/bim-core | awk '{ print $1 }') speedtest_go_tar_download_sha256=$(sha256sum "$work_dir"/speedtest-go.tar.gz | awk '{ print $1 }') librespeed_cli_tar_download_sha256=$(sha256sum "$work_dir"/librespeed-cli.tar.gz | awk '{ print $1 }') if [ "$speedtest_cli_tar_download_sha256" != "$(eval "echo \$speedtest_cli_tar_${speedtest_cli_arch}_sha256")" ]; then printf "${red}%-s${endc}\n" "经检测,speedtest-cli的SHA-256与官方不符,方便的话欢迎到GitHub反馈" exit 1 fi if [ "$bim_core_tar_download_sha256" != "$(eval "echo \$bim_core_tar_${bim_core_arch}_sha256")" ] && [ -n "$bim_core_arch" ]; then printf "${red}%-s${endc}\n" "经检测,bim-core的SHA-256与官方不符,方便的话欢迎到GitHub反馈" exit 1 fi if [ "$speedtest_go_tar_download_sha256" != "$(eval "echo \$speedtest_go_tar_${speedtest_go_arch}_sha256")" ]; then printf "${red}%-s${endc}\n" "经检测,speedtest-go的SHA-256与官方不符,方便的话欢迎到GitHub反馈" exit 1 fi if [ "$librespeed_cli_tar_download_sha256" != "$(eval "echo \$librespeed_cli_tar_${librespeed_cli_arch}_sha256")" ]; then printf "${red}%-s${endc}\n" "经检测,librespeed-cli的SHA-256与官方不符,方便的话欢迎到GitHub反馈" exit 1 fi } ########## 解压程序 ########## _unzip_tar() { tar -xf "$work_dir"/speedtest-cli.tgz -C "$work_dir" tar -xf "$work_dir"/speedtest-go.tar.gz -C "$work_dir" tar -xf "$work_dir"/librespeed-cli.tar.gz -C "$work_dir" chmod +x "$work_dir"/bim-core } ########## 获取节点列表 ########## _get_node_list() { # 节点集合 local node_url node_selection_1 node_selection_2 node_selection_3 first_line second_line _print_banner_1 echo -e "${blue}↓ ↓ ↓ ↓ ↓ ↓ 测速节点列表 ↓ ↓ ↓ ↓ ↓ ↓${endc}" echo -e "0. \033[1m自定义测速节点\033[0m" curl -s -L "$node_set" | awk '$0!~/http/{print}' printf "${yellow}%-s${endc}" "请输入您想选择的节点序号: " read -r node_selection_1 # 某类节点 if [ "$node_selection_1" -ne 0 ]; then node_url=$(curl -s -L "$node_set" | awk -F. "\$1== $node_selection_1 { getline; print }") curl -o "$work_dir"/all-node.txt -L "$node_url" elif [ "$node_selection_1" -eq 0 ]; then printf "${yellow}%-s${endc}" "请输入您的自定义节点链接(http或本地绝对路径): " read -r node_url_custom # 判断自定义节点是网络链接还是本地文件 # 网络,http if [[ "$node_url_custom" =~ \w{0}https?:// ]]; then first_line="$(curl -s -L "$node_url_custom" | awk -F'.' NR==1'{ print $1 }')" second_line="$(curl -s -L "$node_url_custom" | awk NR==2)" if [ "$first_line" -eq 1 ] &>/dev/null && echo "$second_line" | grep -q -E "\w{0}https?://" &>/dev/null; then node_set="$node_url_custom" curl -s -L "$node_set" | awk '$0!~/http/{print}' printf "${yellow}%-s${endc}" "请输入您想选择的节点序号: " read -r node_selection_2 node_url=$(curl -s -L "$node_set" | awk -F. "\$1== $node_selection_2 { getline; print }") curl -o "$work_dir"/all-node.txt -L "$node_url" else node_url="$node_url_custom" curl -o "$work_dir"/all-node.txt -L "$node_url" fi # 本地,绝对路径 elif [[ "$node_url_custom" =~ \w{0}/{1} ]]; then first_line="$(awk -F'.' NR==1'{ print $1 }' <"$node_url_custom")" second_line="$(awk NR==2 <"$node_url_custom")" if [ "$first_line" -eq 1 ] &>/dev/null && echo "$second_line" | grep -q -E "\w{0}/{1}" &>/dev/null; then node_set="$node_url_custom" awk '$0!~/^\//{print}' <"$node_set" printf "${yellow}%-s${endc}" "请输入您想选择的节点序号: " read -r node_selection_3 node_url="$(awk -F. "\$1== $node_selection_3 { getline; print }" <"$node_set")" cp -f "$node_url" "$work_dir"/all-node.txt else node_url="$node_url_custom" cp -f "$node_url" "$work_dir"/all-node.txt fi fi fi } ########## 分类节点 ########### _classify_node() { # 总行数 local row_num row_num=$(wc -l "$work_dir"/all-node.txt | awk '{print $1}') # 分类 for ((i = 1; i <= row_num; i++)); do # 第一列 local first_column first_column=$(awk -F, NR==${i}'{print $1}' "$work_dir"/all-node.txt) # speedtest-cli if [[ "$first_column" =~ "speedtest-cli" ]]; then awk NR==${i} "$work_dir"/all-node.txt >>"$work_dir"/speedtest-cli-node.txt # bim-core elif [[ "$first_column" =~ "bim-core" ]] && [ -n "$bim_core_arch" ]; then awk NR==${i} "$work_dir"/all-node.txt >>"$work_dir"/bim-core-node.txt # speedtest-go elif [[ "$first_column" =~ "speedtest-go" ]]; then awk NR==${i} "$work_dir"/all-node.txt >>"$work_dir"/speedtest-go-node.txt # librespeed-cli elif [[ "$first_column" =~ "librespeed-cli" ]]; then awk NR==${i} "$work_dir"/all-node.txt >>"$work_dir"/librespeed-cli-node.txt # iperf3 elif [[ "$first_column" =~ "iperf3" ]]; then awk NR==${i} "$work_dir"/all-node.txt >>"$work_dir"/iperf3-node.txt # 自定义的横幅 elif [ "$first_column" != "" ]; then awk NR==${i} "$work_dir"/all-node.txt >>"$work_dir"/banner-custom.txt fi done } ########## speedtest-cli ########## _speedtest_cli_test() { # speedtest-cli的选项、参数 awk -F, '{ print $3 }' "$work_dir"/speedtest-cli-node.txt >"$work_dir"/speedtest-cli-option.txt _filter_option_speedtest_cli "$work_dir"/speedtest-cli-option.txt "$work_dir"/speedtest-cli-option-filter.txt # speedtest-cli测试、输出 option_para="" count="1" while IFS= read -r option_para; do IFS="$old_IFS" local node_name latency jitter download upload local download_c="15" upload_c="15" latency_c="13" jitter_c="13" # speedtest-cli测试 _show_progress_bar & bar_pid="$!" && disown "$bar_pid" timeout --foreground 70 ${work_dir}/speedtest --accept-license --accept-gdpr -f json-pretty ${option_para} >${work_dir}/speedtest-cli-${count}.json 2>${work_dir}/speedtest-cli-${count}-error.json kill "$bar_pid" 2>/dev/null && printf "\r" # speedtest-cli输出 if [ -s "$work_dir"/speedtest-cli-"$count".json ]; then # 节点名称 node_name=$(awk -F, NR=="$count"'{ print $2 }' "$work_dir"/speedtest-cli-node.txt) # 延迟,ms latency="$(awk -F': |,' '/ping/{ getline; getline; print $2 }' "$work_dir"/speedtest-cli-"$count".json)" _check_num "$latency" || latency=" 失败" _check_num "$latency" || latency_c="15" _check_num "$latency" && latency="$(printf "%.2f" "$latency") ms" # 抖动,ms jitter="$(awk -F': |,' '/ping/{ getline; print $2 }' "$work_dir"/speedtest-cli-"$count".json)" _check_num "$jitter" || jitter=" 失败" _check_num "$jitter" || jitter_c="15" _check_num "$jitter" && jitter="$(printf "%.2f" "$jitter") ms" # 下载速度,bps->Mbps download="$(awk -F': |,' '/download/{ getline; print $2 }' "$work_dir"/speedtest-cli-"$count".json)" _check_num "$download" || download=" 失败" _check_num "$download" || download_c="17" _check_num "$download" && download="$(echo "scale=2; $download / 125000" | bc)" && download="$(printf "%.2f" "$download") Mbps" # 上传速度,bps->Mbps upload="$(awk -F': |,' '/upload/{ getline; print $2 }' "$work_dir"/speedtest-cli-"$count".json)" _check_num "$upload" || upload=" 失败" _check_num "$upload" || upload_c="17" _check_num "$upload" && upload="$(echo "scale=2; $upload / 125000" | bc)" && upload="$(printf "%.2f" "$upload") Mbps" # 输出结果 [ -s "$work_dir"/speedtest-cli-"$count".json ] && _check_output fi if grep -q "Too many requests received" "$work_dir"/speedtest-cli-"$count"-error.json; then echo "测速次数过多,暂时被限制,请过一段时间后再进行测试" break fi count=$((count + 1)) done <"$work_dir"/speedtest-cli-option-filter.txt } ########## bim-core ########## _bim_core_test() { # bim-core的选项、参数 awk -F, '{ print $3 }' "$work_dir"/bim-core-node.txt >"$work_dir"/bim-core-option.txt _filter_option_bim_core "$work_dir"/bim-core-option.txt "$work_dir"/bim-core-option-filter.txt # bim-core测试、输出 option_para="" count="1" while IFS= read -r option_para; do IFS="$old_IFS" local node_name latency jitter download upload local download_c="15" upload_c="15" latency_c="13" jitter_c="13" # bim-core测试 _show_progress_bar & bar_pid="$!" && disown "$bar_pid" timeout --foreground 70 ${work_dir}/bim-core ${option_para} >${work_dir}/bim-core-${count}.json 2>${work_dir}/bim-core-${count}-error.json kill "$bar_pid" 2>/dev/null && printf "\r" # bim-core输出 if [ -s "$work_dir"/bim-core-"$count".json ]; then # 节点名称 node_name=$(awk -F, NR=="$count"'{ print $2 }' "$work_dir"/bim-core-node.txt) # 延迟,ms latency="$(awk '{ print $5 }' "$work_dir"/bim-core-"$count".json | awk -F',' '{ print $1 }')" _check_num "$latency" || latency=" 失败" _check_num "$latency" || latency_c="15" _check_num "$latency" && latency="$(printf "%.2f" "$latency") ms" # 抖动,ms jitter="$(awk '{ print $6 }' "$work_dir"/bim-core-"$count".json)" _check_num "$jitter" || jitter=" 失败" _check_num "$jitter" || jitter_c="15" _check_num "$jitter" && jitter="$(printf "%.2f" "$jitter") ms" # 下载速度 download="$(awk '{ print $3 }' "$work_dir"/bim-core-"$count".json | awk -F',' '{ print $1 }')" _check_num "$download" || download=" 失败" _check_num "$download" || download_c="17" _check_num "$download" && download="$(printf "%.2f" "$download") Mbps" # 上传速度 upload="$(awk '{ print $1 }' "$work_dir"/bim-core-"$count".json | awk -F',' '{ print $1 }')" _check_num "$upload" || upload=" 失败" _check_num "$upload" || upload_c="17" _check_num "$upload" && upload="$(printf "%.2f" "$upload") Mbps" # 输出结果 [ -s "$work_dir"/bim-core-"$count".json ] && _check_output fi count=$((count + 1)) done <"$work_dir"/bim-core-option-filter.txt } ########## speedtest-go ########## _speedtest_go_test() { # speedtest-go的选项、参数 awk -F, '{ print $3 }' "$work_dir"/speedtest-go-node.txt >"$work_dir"/speedtest-go-option.txt _filter_option_speedtest_go "$work_dir"/speedtest-go-option.txt "$work_dir"/speedtest-go-option-filter.txt # speedtest-go测试、输出 option_para="" count="1" while IFS= read -r option_para; do IFS="$old_IFS" local node_name latency jitter download upload local download_c="15" upload_c="15" latency_c="13" jitter_c="13" # speedtest-go测试 _show_progress_bar & bar_pid="$!" && disown "$bar_pid" timeout --foreground 70 ${work_dir}/speedtest-go ${option_para} >${work_dir}/speedtest-go-${count}.json 2>${work_dir}/speedtest-go-${count}-error.json kill "$bar_pid" 2>/dev/null && printf "\r" # speedtest-go输出 if [ -s "$work_dir"/speedtest-go-"$count".json ] && ! grep -q "Fatal" "$work_dir"/speedtest-go-"$count".json; then # 节点名称 node_name="$(awk -F, NR=="$count"'{ print $2 }' "$work_dir"/speedtest-go-node.txt)" # 延迟,ms latency="$(awk -F' |ms|µs' '/Latency/{ print $3 }' "$work_dir"/speedtest-go-"$count".json)" if awk -F'Latency: ' /Latency/'{ print $2 }' "$work_dir"/speedtest-go-"$count".json | awk -F' Jitter: ' '{ print $1 }' | grep -q µs; then latency_unit="µs" elif awk -F'Latency: ' /Latency/'{ print $2 }' "$work_dir"/speedtest-go-"$count".json | awk -F' Jitter: ' '{ print $1 }' | grep -q ms; then latency_unit="ms" fi _check_num "$latency" || latency=" 失败" _check_num "$latency" || latency_c="15" _check_num "$latency" && latency="$(printf "%.2f" "$latency") $latency_unit" # 抖动,ms jitter="$(awk -F' |ms|µs' '/Jitter/{ print $6 }' "$work_dir"/speedtest-go-"$count".json)" if awk -F'Jitter: ' /Jitter/'{ print $2 }' "$work_dir"/speedtest-go-"$count".json | awk -F' Min: ' '{ print $1 }' | grep -q µs; then jitter_unit="µs" elif awk -F'Jitter: ' /Jitter/'{ print $2 }' "$work_dir"/speedtest-go-"$count".json | awk -F' Min: ' '{ print $1 }' | grep -q ms; then jitter_unit="ms" fi _check_num "$jitter" || jitter=" 失败" _check_num "$jitter" || jitter_c="15" _check_num "$jitter" && jitter="$(printf "%.2f" "$jitter") $jitter_unit" # 下载、上传速度,Mbps download="$(awk -F' |Mbps' '/Download/{ print $3 }' "$work_dir"/speedtest-go-"$count".json)" _check_num "$download" || download=" 失败" _check_num "$download" || download_c="17" _check_num "$download" && download="$(printf "%.2f" "$download") Mbps" upload="$(awk -F' |Mbps' '/Upload/{ print $3 }' "$work_dir"/speedtest-go-"$count".json)" _check_num "$upload" || upload=" 失败" _check_num "$upload" || upload_c="17" _check_num "$upload" && upload="$(printf "%.2f" "$upload") Mbps" _check_option "--no-upload" "$option_para" && upload=" 跳过" _check_option "--no-upload" "$option_para" && upload_c="17" _check_option "--no-download" "$option_para" && download=" 跳过" _check_option "--no-download" "$option_para" && download_c="17" # 输出结果 [ -s "$work_dir"/speedtest-go-"$count".json ] && _check_output fi count=$((count + 1)) done <"$work_dir"/speedtest-go-option-filter.txt } ########## librespeed-cli ########## _librespeed_cli_test() { # librespeed-cli的选项、参数 awk -F, '{ print $3 }' "$work_dir"/librespeed-cli-node.txt >"$work_dir"/lbrespeed-cli-option.txt _filter_option_librespeed_cli "$work_dir"/lbrespeed-cli-option.txt "$work_dir"/lbrespeed-cli-option-filter.txt # librespeed-cli测试、输出 option_para="" count="1" while IFS= read -r option_para; do IFS="$old_IFS" local node_name latency jitter download upload local download_c="15" upload_c="15" latency_c="13" jitter_c="13" # librespeed-cli测试 _show_progress_bar & bar_pid="$!" && disown "$bar_pid" timeout --foreground 70 ${work_dir}/librespeed-cli --json ${option_para} >${work_dir}/librespeed-cli-${count}.json 2>${work_dir}/librespeed-cli-${count}-error.json kill "$bar_pid" 2>/dev/null && printf "\r" # librespeed-cli输出 if [ -s "$work_dir"/librespeed-cli-"$count".json ]; then # 节点名称 node_name=$(awk -F, NR=="$count"'{ print $2 }' "$work_dir"/librespeed-cli-node.txt) # 延迟,ms latency="$(awk -F'"ping":' '{ print $2 }' "$work_dir"/librespeed-cli-"$count".json | awk -F',' '{ print $1 }')" _check_num "$latency" || latency=" 失败" _check_num "$latency" || latency_c="15" _check_num "$latency" && latency="$(printf "%.2f" "$latency") ms" # 抖动,ms jitter="$(awk -F'"jitter":' '{ print $2 }' "$work_dir"/librespeed-cli-"$count".json | awk -F',' '{ print $1 }')" _check_num "$jitter" || jitter=" 失败" _check_num "$jitter" || jitter_c="15" _check_num "$jitter" && jitter="$(printf "%.2f" "$jitter") ms" # 下载、上传速度,Mbps download="$(awk -F'"download":' '{ print $2 }' "$work_dir"/librespeed-cli-"$count".json | awk -F',' '{ print $1 }')" _check_num "$download" || download=" 失败" _check_num "$download" || download_c="17" _check_num "$download" && download="$(printf "%.2f" "$download") Mbps" upload="$(awk -F'"upload":' '{ print $2 }' "$work_dir"/librespeed-cli-"$count".json | awk -F',' '{ print $1 }')" _check_num "$upload" || upload=" 失败" _check_num "$upload" || upload_c="17" _check_num "$upload" && upload="$(printf "%.2f" "$upload") Mbps" _check_option "--no-upload" "$option_para" && upload=" 跳过" _check_option "--no-upload" "$option_para" && upload_c="17" _check_option "--no-download" "$option_para" && download=" 跳过" _check_option "--no-download" "$option_para" && download_c="17" # 输出结果 [ -s "$work_dir"/librespeed-cli-"$count".json ] && _check_output fi count=$((count + 1)) done <"$work_dir"/lbrespeed-cli-option-filter.txt } ########## iperf3 ########## _iperf3_test() { # iperf3的选项、参数 awk -F',' '{ print $3 }' "$work_dir"/iperf3-node.txt >"$work_dir"/iperf3-option.txt _filter_option_iperf3 "$work_dir"/iperf3-option.txt "$work_dir"/iperf3-option-filter.txt # iperf3测试、输出 option_para="" count="1" while IFS= read -r option_para; do IFS="$old_IFS" local node_name download upload latency jitter local download_c="15" upload_c="15" latency_c="13" jitter_c="13" # iperf3测试 if _check_option "--up-and-down" "$option_para"; then # 双向 option_para="$(echo "$option_para" | awk -F'--up-and-down' '{ print $1 $2 }')" # 节点名称 node_name=$(awk -F, NR=="$count"'{ print $2 }' "$work_dir"/iperf3-node.txt) # 延迟,ms latency=" 跳过" latency_c="15" # 抖动,ms jitter=" 跳过" jitter_c="15" # 上传 local i_busy for ((i_busy = 1; i_busy <= 65; i_busy++)); do _show_progress_bar & bar_pid="$!" && disown "$bar_pid" timeout --foreground 70 iperf3 -f m ${option_para} >${work_dir}/iperf3-${count}.json 2>${work_dir}/iperf3-${count}-error.json kill "$bar_pid" 2>/dev/null && printf "\r" if grep -q "busy" "$work_dir"/iperf3-"$count"-error.json; then sleep 0.5 fi if [ -s "$work_dir"/iperf3-"$count".json ]; then break fi done # 上传速度,Mbps if [ -s "$work_dir"/iperf3-"$count".json ]; then upload="$(awk '{ rows[NR] = $0 } END{ print rows[NR-3] }' "$work_dir"/iperf3-"$count".json | awk -F'Bytes ' '{ print $2 }' | awk '{ print $1 }')" _check_num "$upload" || upload=" 失败" _check_num "$upload" || upload_c="17" _check_num "$upload" && upload="$(printf "%.2f" "$upload") Mbps" else upload=" 跳过" && upload_c="17" fi # 下载 for ((i_busy = 1; i_busy <= 65; i_busy++)); do _show_progress_bar & bar_pid="$!" && disown "$bar_pid" timeout --foreground 70 iperf3 -f m -R ${option_para} >${work_dir}/iperf3-${count}.json 2>${work_dir}/iperf3-${count}-error.json kill "$bar_pid" 2>/dev/null && printf "\r" if grep -q "busy" "$work_dir"/iperf3-"$count"-error.json; then sleep 0.5 fi if [ -s "$work_dir"/iperf3-"$count".json ]; then break fi done # 下载速度,Mbps if [ -s "$work_dir"/iperf3-"$count".json ]; then download="$(awk '{ rows[NR] = $0 } END{ print rows[NR-2] }' "$work_dir"/iperf3-"$count".json | awk -F'Bytes ' '{ print $2 }' | awk '{ print $1 }')" _check_num "$download" || download=" 失败" _check_num "$download" || download_c="17" _check_num "$download" && download="$(printf "%.2f" "$download") Mbps" else upload=" 跳过" && upload_c="17" fi # 输出结果 [ -s "$work_dir"/iperf3-"$count".json ] && _check_output else # 单向 for ((i_busy = 1; i_busy <= 65; i_busy++)); do _show_progress_bar & bar_pid="$!" && disown "$bar_pid" timeout --foreground 70 iperf3 -f m ${option_para} >${work_dir}/iperf3-${count}.json 2>${work_dir}/iperf3-${count}-error.json kill "$bar_pid" 2>/dev/null && printf "\r" if grep -q "busy" "$work_dir"/iperf3-"$count"-error.json; then sleep 0.5 fi if [ -s "$work_dir"/iperf3-"$count".json ]; then break fi done # iperf3输出 if [ -s "$work_dir"/iperf3-"$count".json ]; then # 节点名称 node_name=$(awk -F, NR=="$count"'{ print $2 }' "$work_dir"/iperf3-node.txt) # 延迟,ms latency=" 跳过" latency_c="15" # 抖动,ms jitter=" 跳过" jitter_c="15" # 下载、上传速度,Mbps upload="$(awk '{ rows[NR] = $0 } END{ print rows[NR-3] }' "$work_dir"/iperf3-"$count".json | awk -F'Bytes ' '{ print $2 }' | awk '{ print $1 }')" _check_num "$upload" || upload=" 失败" _check_num "$upload" || upload_c="17" _check_num "$upload" && upload="$(printf "%.2f" "$upload") Mbps" download="$(awk '{ rows[NR] = $0 } END{ print rows[NR-2] }' "$work_dir"/iperf3-"$count".json | awk -F'Bytes ' '{ print $2 }' | awk '{ print $1 }')" _check_num "$download" || download=" 失败" _check_num "$download" || download_c="17" _check_num "$download" && download="$(printf "%.2f" "$download") Mbps" _check_option "-R" "$option_para" && upload=" 跳过" _check_option "-R" "$option_para" && upload_c="17" _check_option "-R" "$option_para" || download=" 跳过" _check_option "-R" "$option_para" || download_c="17" # 输出结果 [ -s "$work_dir"/iperf3-"$count".json ] && _check_output fi fi count=$((count + 1)) done <"$work_dir"/iperf3-option-filter.txt } ########## 整理选项、参数 ########## # 不带参数的选项,包括-/-- _filter_option_0_para() { if [ "$(echo "$line_input" | awk "{ print $"$column_count" }")" == "$1" ] || [ "$(echo "$line_input" | awk "{ print $"$column_count" }")" == "$2" ]; then line_output="$line_output $1" fi } # 带一个参数的选项,包括-/-- _filter_option_1_para() { if [ "$(echo "$line_input" | awk "{ print $"$column_count" }")" == "$1" ] || [ "$(echo "$line_input" | awk "{ print $"$column_count" }")" == "$2" ]; then column_count=$((column_count + 1)) line_output="$line_output $1 $(echo "$line_input" | awk "{ print $"$column_count" }")" fi } # bim-core专用,处理为链接的参数 _filter_option_bim_core_1() { if [[ "$(echo "$line_input" | awk "{ print $"$column_count" }")" =~ http ]]; then line_output="$line_output $(echo "$line_input" | awk "{ print $"$column_count" }")" fi } # iperf3专用,处理-p选项 _filter_option_iperf3_1() { local port if [ "$(echo "$line_input" | awk "{ print $"$column_count" }")" == "-p" ] || [ "$(echo "$line_input" | awk "{ print $"$column_count" }")" == "--port" ]; then column_count=$((column_count + 1)) port=$(echo "$line_input" | awk "{ print $"$column_count" }") if [[ "$port" =~ ^[0-9]+$ ]]; then # 一个端口 line_output="$line_output $1 $port" else # 多个端口,随机一个 port=$(shuf -n 1 -i $port) line_output="$line_output $1 $port" fi fi } # speedtest-cli _filter_option_speedtest_cli() { local line_input line_output column_count column_all_count local file_input="$1" file_output="$2" while IFS= read -r line_input; do IFS="$old_IFS" line_output="" column_all_count="$(echo "$line_input" | awk '{ print NF }')" for ((column_count = 1; column_count <= column_all_count; column_count++)); do _filter_option_1_para -s --server-id _filter_option_1_para -o --host done echo "$line_output" >>"$file_output" done <"$file_input" } # bim-core _filter_option_bim_core() { local line_input line_output column_count column_all_count local file_input="$1" file_output="$2" while IFS= read -r line_input; do IFS="$old_IFS" line_output="" column_all_count="$(echo "$line_input" | awk '{ print NF }')" for ((column_count = 1; column_count <= column_all_count; column_count++)); do _filter_option_1_para -6 --ipv6 _filter_option_1_para -m --multi _filter_option_bim_core_1 done echo "$line_output" >>"$file_output" done <"$file_input" } # speedtest-go _filter_option_speedtest_go() { local line_input line_output column_count column_all_count local file_input="$1" file_output="$2" while IFS= read -r line_input; do IFS="$old_IFS" line_output="" column_all_count="$(echo "$line_input" | awk '{ print NF }')" for ((column_count = 1; column_count <= column_all_count; column_count++)); do _filter_option_1_para -s --server _filter_option_1_para --custom-url --custom-url _filter_option_1_para -t --thread _filter_option_1_para --ping-mode --ping-mode _filter_option_0_para -m --multi _filter_option_0_para --no-download --no-download _filter_option_0_para --no-upload --no-upload done echo "$line_output" >>"$file_output" done <"$file_input" } # librespeed-cli _filter_option_librespeed_cli() { local line_input line_output column_count column_all_count local file_input="$1" file_output="$2" while IFS= read -r line_input; do IFS="$old_IFS" line_output="" column_all_count="$(echo "$line_input" | awk '{ print NF }')" for ((column_count = 1; column_count <= column_all_count; column_count++)); do _filter_option_1_para --server-json --server-json _filter_option_1_para --local-json --local-json _filter_option_1_para --server --server _filter_option_1_para --duration --duration _filter_option_0_para --no-download --no-download _filter_option_0_para --no-upload --no-upload _filter_option_0_para --no-icmp --no-icmp _filter_option_0_para -4 --ipv4 _filter_option_0_para -6 --ipv6 done echo "$line_output" >>"$file_output" done <"$file_input" } # iperf3 _filter_option_iperf3() { local line_input line_output column_count column_all_count local file_input="$1" file_output="$2" while IFS= read -r line_input; do IFS="$old_IFS" line_output="" column_all_count="$(echo "$line_input" | awk '{ print NF }')" for ((column_count = 1; column_count <= column_all_count; column_count++)); do _filter_option_1_para -c -c _filter_option_1_para -P --parallel _filter_option_1_para -i --interval _filter_option_1_para -t --time _filter_option_1_para -O --omit _filter_option_0_para -R --reverse _filter_option_0_para -u --udp _filter_option_0_para -4 --version4 _filter_option_0_para -6 --version6 _filter_option_0_para --up-and-down --up-and-down _filter_option_iperf3_1 -p --port done echo "$line_output" >>"$file_output" done <"$file_input" } ########## 检测选项,用于预判测速结果 ########## _check_option() { local option_input="$1" options_input="$2" if echo "$options_input" | grep -q -- "$option_input"; then return 0 else return 1 fi } _check_num() { local num_input="$1" if [[ "$num_input" =~ ^[0-9]+(\.[0-9]+)?$ ]] && ! [[ "$num_input" =~ ^0+(\.0+)?$ ]]; then return 0 else return 1 fi } ########## 输出 ########## _check_output() { local i_check_output local count_check_output="0" for i_check_output in $(echo "$download $upload $latency $jitter"); do if [[ "$i_check_output" =~ 失败 ]]; then count_check_output=$((count_check_output + 1)) elif [[ "$i_check_output" =~ 跳过 ]]; then count_check_output=$((count_check_output + 1)) fi done [ "$count_check_output" -ne 4 ] && printf "${yellow}%-s${green}%-${download_c}s${cyan}%-${upload_c}s${blue}%-${latency_c}s${purple}%-${jitter_c}s${endc}\n" "$node_name" "$download" "$upload" "$latency" "$jitter" } ########## 删除残余文件 ########## _rm_dir() { _print_banner_4 rm -rf "$work_dir" exit 0 } ########## debug ########## _debug() { local debug_tar_name if [[ "$debug_flag" -eq 1 ]]; then { echo "OS info:" cat /etc/os-release echo "Shell info:" echo "$SHELL" echo "User info:" echo "$UID" echo "Loc info:" echo "$loc" } >$work_dir/info.txt debug_tar_name="sp-debug-$(TZ=Asia/Shanghai date +"%Y%m%d%H%M%S").tar" touch $work_dir/1.json tar -cf "$work_dir/$debug_tar_name" ${work_dir}/*.{json,txt} printf "\n${red}%-s${endc}\n" "方便的话欢迎将到GitHub反馈下述结果:" curl -X POST -s -Fc=@"$work_dir/$debug_tar_name" -Fn="${debug_tar_name%.*}" -Fe=90d 'https://pastebin.888853.xyz' echo fi } ########## main ########## _main() { trap '{ kill ${bar_pid} 2>/dev/null; printf "\r"; _debug; _rm_dir; }' EXIT _check_architecture _constant _print_banner_1 _check_user _check_region _check_package tar tar _check_package curl curl _check_package iperf3 iperf3 _check_package bc bc _make_dir _download_tar _check_tar_sha256 _unzip_tar clear _get_node_list _classify_node clear _print_banner_1 [ -s "$work_dir"/banner-custom.txt ] && _print_banner_2 _print_banner_3 [ -s "$work_dir"/speedtest-cli-node.txt ] && _speedtest_cli_test [ -s "$work_dir"/bim-core-node.txt ] && _bim_core_test [ -s "$work_dir"/speedtest-go-node.txt ] && _speedtest_go_test [ -s "$work_dir"/librespeed-cli-node.txt ] && _librespeed_cli_test [ -s "$work_dir"/iperf3-node.txt ] && _iperf3_test } #################### for arg in "$@"; do if [[ "$arg" == "--debug" ]]; then debug_flag="1" fi done _main