Files
2026-08-13 09:31:12 +08:00

797 lines
18 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# 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:建议安装/启用 curlGit-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}s0=无限)"
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