#!/bin/bash # shellcheck disable=SC2155 if ! type "include" > /dev/null 2>&1; then include() { . "$(cd "$(dirname "$1")" && pwd)/${2}"; }; fi include "${BASH_SOURCE[0]}" ../pragma_once.bash if ! pragma_once ${BASH_SOURCE[0]}; then return 0; fi include "${BASH_SOURCE[0]}" ./global_rely/export.bash # shellcheck source=./global_rely/export.bash # 这种是为了语法提示 # shellcheck source=./global_rely/log.bash # shellcheck source=./global_rely/stack_trace.bash check_baidu_network() { local baidu_domain="www.baidu.com" # 要检测的目标域名 echo "正在检测是否能连接到 $baidu_domain..." # 使用 ping 命令检测是否能连接到 baidu.com if ping -c 1 "$baidu_domain" > /dev/null 2>&1; then echo "$baidu_domain 网络连接正常。" return 0 # 连接正常 else echo "无法连接到 $baidu_domain,网络不可达。" return 1 # 网络不可达 fi } # 用法: # chromium_net_check_report # 检测一次并输出报告 # chromium_net_check_report --wait # 循环等待,直到“任一方式可用”为止,并持续输出报告 # chromium_net_check_report --wait 300 # 最多等待 300 秒 # # 报告包含: # 1) DNS 解析是否成功 # 2) ping(ICMP) 是否通(注意:TUN/代理下可能一直不通) # 3) curl HTTPS HEAD 是否通(更贴近实际拉代码/访问网页) # chromium_net_check_report() { local host="chromium.googlesource.com" local wait_mode=0 local timeout=0 if [[ "${1:-}" == "--wait" ]]; then wait_mode=1 timeout="${2:-0}" # 0 = 无限等 fi # ping 参数兼容:Windows (MSYS/Git-Bash) 用 -n/-w(ms),Linux/macOS 用 -c/-W(s) local ping_count_flag="-c" local ping_timeout_flag="-W" local ping_timeout_val="1" case "$(uname -s 2>/dev/null | tr '[:upper:]' '[:lower:]')" in mingw*|msys*|cygwin*) ping_count_flag="-n" ping_timeout_flag="-w" ping_timeout_val="1000" # ms ;; esac _ts() { date "+%Y-%m-%d %H:%M:%S" 2>/dev/null || echo "" } _dns_check() { # 尽量不用 nslookup 输出噪音:只判断能不能解析出 IP if command -v getent >/dev/null 2>&1; then getent hosts "$host" >/dev/null 2>&1 return $? elif command -v nslookup >/dev/null 2>&1; then nslookup "$host" >/dev/null 2>&1 return $? else return 127 fi } _ping_check() { ping "${ping_count_flag}" 1 "${ping_timeout_flag}" "${ping_timeout_val}" "$host" >/dev/null 2>&1 return $? } _curl_check() { if ! command -v curl >/dev/null 2>&1; then return 127 fi curl -fsSI --max-time 6 "https://${host}/" >/dev/null 2>&1 return $? } _one_report() { local dns_rc ping_rc curl_rc local dns_status ping_status curl_status local hint=() dns_rc=0; ping_rc=0; curl_rc=0 _dns_check; dns_rc=$? _ping_check; ping_rc=$? _curl_check; curl_rc=$? case "$dns_rc" in 0) dns_status="OK" ;; 127) dns_status="N/A(no nslookup/getent)" ;; *) dns_status="FAIL" ;; esac case "$ping_rc" in 0) ping_status="OK" ;; *) ping_status="FAIL" ;; esac case "$curl_rc" in 0) curl_status="OK" ;; 127) curl_status="N/A(no curl)" ;; *) curl_status="FAIL" ;; esac echo "==================== Chromium Network Report ====================" echo "Time: $(_ts)" echo "Host: ${host}" echo "------------------------------------------------------------------" echo "DNS : ${dns_status}" echo "PING : ${ping_status}" echo "CURL : ${curl_status} (HTTPS HEAD https://${host}/)" echo "------------------------------------------------------------------" # 解释/建议(按组合给) if [[ "$dns_rc" != 0 && "$dns_rc" != 127 ]]; then hint+=("DNS 解析失败:检查系统 DNS/Clash DNS 设置,或是否被劫持/污染。") fi if [[ "$curl_rc" == 0 ]]; then hint+=("HTTPS 可访问:用于 gclient/git 拉取通常已经足够。") else if [[ "$curl_rc" == 127 ]]; then hint+=("未检测到 curl:建议安装/启用 curl(Git-Bash 通常自带)。") else hint+=("HTTPS 不可访问:通常表示代理/TUN 未生效或证书/网络被拦。") fi fi if [[ "$ping_rc" != 0 ]]; then hint+=("PING 不通:在 Clash TUN/代理下可能正常(ICMP 常被丢弃)。不要只以 ping 为唯一标准。") fi if [[ "${#hint[@]}" -gt 0 ]]; then echo "Notes:" local i for i in "${hint[@]}"; do echo " - $i" done fi echo "==================================================================" # 返回码策略: # 0 = curl OK(认为网络可用) # 1 = curl FAIL(不可用) if [[ "$curl_rc" == 0 ]]; then return 0 fi return 1 } if [[ "$wait_mode" -eq 0 ]]; then _one_report return $? fi local start_ts now_ts elapsed start_ts=$(date +%s 2>/dev/null || echo 0) echo "[WAIT] 循环检测中:直到 CURL 可访问为止(timeout=${timeout}s,0=无限)" while true; do if _one_report; then echo "[DONE] CURL 已可访问:网络就绪" return 0 fi if [[ "$timeout" -gt 0 && "$start_ts" -ne 0 ]]; then now_ts=$(date +%s 2>/dev/null || echo 0) elapsed=$((now_ts - start_ts)) if [[ "$elapsed" -ge "$timeout" ]]; then echo "[TIMEOUT] 等待超过 ${timeout}s,仍不可访问 https://${host}/" return 2 fi fi sleep 3 done } print_important_var() { local var=$1 # 传入的变量名 local val=${!var} # 使用 eval 来获取变量的值 echo "【${var}】=== 【${val}】" } load_PATH(){ local tmp merge_args tmp ":" "$@" export PATH="${tmp}${PATH:+:$PATH}" } load_LIBRARY_PATH(){ local tmp merge_args tmp ":" "$@" export LIBRARY_PATH="${tmp}${LIBRARY_PATH:+:$LIBRARY_PATH}" } load_LD_LIBRARY_PATH(){ local tmp merge_args tmp ":" "$@" export LD_LIBRARY_PATH="${tmp}${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" } load_ALL(){ load_PATH "$@" load_LIBRARY_PATH "$@" load_LD_LIBRARY_PATH "$@" } save_env() { export saved_PATH="$PATH" export saved_LIBRARY_PATH="$LIBRARY_PATH" export saved_LD_LIBRARY_PATH="$LD_LIBRARY_PATH" } restore_env() { export PATH="$saved_PATH" export LIBRARY_PATH="$saved_LIBRARY_PATH" export LD_LIBRARY_PATH="$saved_LD_LIBRARY_PATH" } # 公用函数:打印指定环境变量的路径 print_env_path() { local env_var="$1" # 环境变量名 local env_value="${!env_var}" # 获取环境变量的值 # 输出环境变量的路径 log_info "[ $env_var ]:" IFS=":" read -r -a paths <<< "$env_value" i=0 for path in "${paths[@]}"; do # 使用 printf 格式化 $i,确保它是 3 位,右对齐 formatted_i=$(printf "%2d" "$i") log_info "$formatted_i: $path" ((i++)) || true done } print_env() { log_trace "========================== 【 打印环境变量 】 ==========================" print_env_path "PATH" print_env_path "LD_LIBRARY_PATH" print_env_path "LD_RUN_PATH" print_env_path "LIBRARY_PATH" log_trace "========================== 【 打印环境变量 】 ==========================" } print_remain_args(){ local err=$1 shift 1 local i=0 log_error "msg:${err}" for param in "$@"; do log_error "[%2d] $param" $i ((i++)) done } # 使用方式 # if pragma_once "${BASH_SOURCE[0]}"; then # fi centos7_6_init_iso_root() { sudo yum --disablerepo=* --enablerepo=wyc groupinstall "Development Tools" -y sudo yum --disablerepo=* --enablerepo=wyc install gmp-devel mpfr-devel libmpc-devel openssl mesa-libGL-devel -y } check_directory() { local dir="$1" if [ ! -d "$dir" ]; then echo "错误: 目录不存在: $dir" return 1 fi return 0 } check_directory_var() { local var_name="$1" local dir="${!var_name}" if [ -z "$var_name" ]; then echo "错误: 未传入变量名" return 1 fi if [ -z "$dir" ]; then echo "错误: 变量未定义或为空: $var_name" return 1 fi if [ ! -d "$dir" ]; then echo "错误: 目录不存在: $var_name=$dir" return 1 fi return 0 } check_file() { local file="$1" if [ ! -f "$file" ]; then echo "错误: 文件不存在: $file" return 1 fi return 0 } check_file_ex() { local prefix="$1" local file="$2" if [ ! -f "$file" ]; then echo "【${prefix}】 错误: 文件不存在: $file" return 1 fi return 0 } check_file_var() { local var_name="$1" local file="${!var_name}" if [ -z "$var_name" ]; then echo "错误: 未传入变量名" return 1 fi if [ -z "$file" ]; then echo "错误: 变量未定义或为空: $var_name" return 1 fi if [ ! -f "$file" ]; then echo "错误: 文件不存在: $var_name=$file" return 1 fi return 0 } is_absolute_directory() { local dir_path="$1" if [[ "$dir_path" != /* ]] || [ ! -d "$dir_path" ]; then return 1 fi return 0 } is_absolute_file() { local file_path="$1" if [[ "$file_path" != /* ]] || [ ! -f "$file_path" ]; then return 1 fi return 0 } is_absolute_elf() { local path="$1" if [[ "$path" != /* ]]; then echo "Not an absolute path: $path" return 1 fi if [[ ! -f "$path" ]]; then echo "File not found: $path" return 1 fi if ! file "$path" | grep -q 'ELF'; then echo "Not an ELF file: $path" return 1 fi return 0 } is_valid_absolute_path_format() { local input="$1" if [[ "$input" =~ ^(/[^:/]+)+(:/[^:/]+)+$ ]]; then return 0 fi echo "Invalid format: $input" return 1 } #!/bin/bash # 压缩函数:将目录压缩为 .tar.gz 格式 compress_directory_tar_gz() { local source_dir="$1" # 源目录 local install_root="$2" # 目标目录 local archive_name="$3" # 压缩包名称 if [ ! -d "$source_dir" ]; then echo "compress_directory Error: Source directory '$source_dir' does not exist!" return 1 fi echo "Compressing '$source_dir' to '$install_root/$archive_name'..." # 去除硬链接 # local workdir="$(dirname "$source_dir")" # local a2="$(basename "$source_dir")" tar --hard-dereference -czvf "$install_root/$archive_name" -C "${source_dir}" . if [ $? -eq 0 ]; then echo "Compression successful! $install_root/$archive_name" else echo "Compression failed! $install_root/$archive_name" return 1 fi } # 解压函数:将 .tar.gz 文件解压到目标目录 compress_directory_tar_gz_copy() { local source_dir="$1" local install_root="$2" local archive_name="$3" if [ ! -d "$source_dir" ]; then echo "compress_directory Error: Source directory '$source_dir' does not exist!" return 1 fi echo "Compressing '$source_dir' to '$install_root/$archive_name'..." tar --dereference --hard-dereference -czvf "$install_root/$archive_name" -C "${source_dir}" . if [ $? -eq 0 ]; then echo "Compression successful! $install_root/$archive_name" else echo "Compression failed! $install_root/$archive_name" return 1 fi } # 示例用法 # compress_directory "/path/to/source_directory" "/path/to/your/install_root" "your_archive_name.tar.gz" # decompress_archive "/path/to/your/install_root/your_archive_name.tar.gz" "/path/to/target_directory" # 通用检查函数:检查值是否在给定范围内 check_value_in_range() { local param_name="$1" # 参数名称 shift # 移除参数名称,剩下的是有效范围 # 获取参数的当前值 local value="${!param_name}" # 遍历所有范围值 for range_value in "$@"; do if [[ "$value" == "$range_value" ]]; then return 0 # 找到匹配的值,返回成功 fi done # 如果没有找到匹配值,打印错误并退出 log_error "ERROR: '$param_name' has invalid value '$value'. Valid values are: $*" exit 77 } check_ret_exit() { local ret=$? local msg="$1" if (( ret != 0 )); then if [[ -z "${msg//[[:space:]]/}" ]]; then msg="命令执行失败" fi log_error "${msg} ret=${ret}" exit "$ret" fi } clear_dir_contents() { local dir="$1" # 参数为空时直接报错返回 if [[ -z "$dir" ]]; then echo "[ERROR] clear_dir_contents: 目录参数不能为空" >&2 return 1 fi # 目录不存在时跳过,不报错 if [[ ! -d "$dir" ]]; then echo "[INFO] 目录不存在,跳过清空: $dir" return 0 fi # 删除普通文件和普通目录 rm -rf "${dir:?}/"* # 删除隐藏文件/目录(例如 .git、.cache),但不匹配 . 和 .. rm -rf "${dir:?}/".[!.]* # 删除以两个点开头的隐藏文件/目录,但仍然不会匹配 .. 本身 rm -rf "${dir:?}/"..?* echo "[OK] 已清空目录内容: $dir" } check_and_create_dir_var() { local dir_var="$1" local dir_path="${!dir_var:-}" if [[ -z "$dir_path" ]]; then log_error "ERROR: directory variable is empty: %s" "$dir_var" return 1 fi if [[ -d "$dir_path" ]]; then return 0 fi mkdir -p "$dir_path" || { log_error "ERROR: failed to create directory: %s=%s" "$dir_var" "$dir_path" return 1 } } check_and_create_dir() { local dir="$1" if [[ -z "${dir//[[:space:]]/}" ]]; then log_error "ERROR: dir is empty" return 4 fi if [[ -d "$dir" ]]; then return 0 fi mkdir -p "$dir" || { log_error "ERROR: failed to create directory: %s" "$dir" return 5 } } # 使用示例 # declare -a files # regex_glob_files files "/ae/bash/0_git_cache_dir/binutils-gdb" "/ae/bash/0_git_cache_dir/binutils-gdb/build" '.*\.(c|h)$' # echo "count: ${#files[@]}" # printf '%s\n' "${files[@]}" # i=0 # for f in "${files[@]}"; do # printf '%d: %s\n' "$i" "$f" # ((i++)) # done regex_glob_files() { if [ $# -lt 4 ]; then return 1 fi local ret_name="$1" local include_path="$2" local exclude_path="$3" local regex_expr="$4" if [ -z "$ret_name" ]; then return 1 fi local -n ret="$ret_name" ret=() if [ -z "$include_path" ] || [ ! -e "$include_path" ]; then return 0 fi local include_abs="" local exclude_abs="" local item="" local base_name="" include_abs=$(realpath "$include_path") if [ -n "$exclude_path" ] && [ -e "$exclude_path" ]; then exclude_abs=$(realpath "$exclude_path") fi if [ -f "$include_abs" ]; then if [ -n "$exclude_abs" ] && [ "$include_abs" = "$exclude_abs" ]; then return 0 fi if [ -n "$regex_expr" ]; then base_name="${include_abs##*/}" if [[ "$base_name" =~ $regex_expr ]]; then ret+=("$include_abs") fi else ret+=("$include_abs") fi return 0 fi if [ -d "$include_abs" ]; then while IFS= read -r item; do if [ -n "$regex_expr" ]; then base_name="${item##*/}" if [[ "$base_name" =~ $regex_expr ]]; then ret+=("$item") fi else ret+=("$item") fi done < <( if [ -n "$exclude_abs" ]; then find "$include_abs" \ \( -path "$exclude_abs" -o -path "$exclude_abs/*" \) -prune -o \ -type f -print else find "$include_abs" -type f -print fi ) return 0 fi return 0 } # * 任意长度任意字符(可为0) *.c a.c, test.c a.cpp # ? 任意1个字符 a?.txt ab.txt a.txt, abc.txt # [abc] 匹配集合中一个字符 file[12].txt file1.txt file3.txt # [a-z] 匹配范围内一个字符 file[a-z].txt filea.txt fileA.txt # [!abc] 不在集合中的一个字符 file[!0-9].txt filea.txt file1.txt glob_files_by_name() { if [ $# -lt 4 ]; then return 1 fi local ret_name="$1" local include_path="$2" local exclude_path="$3" local name_pattern="$4" local -n ret="$ret_name" ret=() [ -z "$include_path" ] && return 0 [ ! -e "$include_path" ] && return 0 local include_abs local exclude_abs="" include_abs=$(realpath "$include_path") if [ -n "$exclude_path" ] && [ -e "$exclude_path" ]; then exclude_abs=$(realpath "$exclude_path") fi if [ -f "$include_abs" ]; then local base_name="${include_abs##*/}" if [[ "$base_name" == $name_pattern ]]; then ret+=("$include_abs") fi return 0 fi while IFS= read -r item; do ret+=("$item") done < <( if [ -n "$exclude_abs" ]; then find "$include_abs" \ \( -path "$exclude_abs" -o -path "$exclude_abs/*" \) -prune -o \ -type f -name "$name_pattern" -print else find "$include_abs" -type f -name "$name_pattern" -print fi ) } # 生成命令行脚本 给某些makefile使用 _generate_case_dispatch_script_begin() { if [ $# -ne 1 ]; then return 1 fi local ret_name="$1" local -n ret="$ret_name" ret="" ret+=$'#!/usr/bin/env bash\n' ret+=$'set -euo pipefail\n' ret+=$'\n' ret+=$'case "${2:-${1:-}}" in\n' } _generate_case_dispatch_script_append_case() { if [ $# -ne 3 ]; then return 1 fi local ret_name="$1" local case_label="$2" local case_value="$3" if [ -z "$case_label" ] || [ -z "$case_value" ]; then return 1 fi local -n ret="$ret_name" ret+="${case_label})"$'\n' printf -v ret '%s printf '\''%%s'\\\\n\'' "%s"\n' "$ret" "$case_value" ret+=$' ;;\n' } _generate_case_dispatch_script_end() { if [ $# -ne 1 ]; then return 1 fi local ret_name="$1" local -n ret="$ret_name" ret+=$'*)\n' ret+=$' echo "unsupported args: $*" >&2\n' ret+=$' exit 1\n' ret+=$' ;;\n' ret+=$'esac\n' ret+=$'exit 0\n' } generate_case_dispatch_script_string() { if [ $# -lt 3 ]; then return 1 fi local ret_name="$1" shift if [ -z "$ret_name" ]; then return 1 fi if [ $(( $# % 2 )) -ne 0 ]; then return 1 fi _generate_case_dispatch_script_begin "$ret_name" || return 1 local case_label="" local case_value="" while [ $# -gt 0 ]; do case_label="$1" case_value="$2" shift 2 _generate_case_dispatch_script_append_case "$ret_name" "$case_label" "$case_value" || return 1 done _generate_case_dispatch_script_end "$ret_name" || return 1 return 0 } return 0