#!/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]}" ../base/export.bash false && source ../base/export.bash insert_after_match() { local file="$1" local pattern="$2" local insert_content="$3" if [[ ! -f "$file" ]]; then echo "File not found: $file" return 75 fi local tmp="${file}.tmp" # 使用 awk 插入(比 sed 更稳) awk 是拓展正则表达式 \(是字面量 echo "insert_content ${insert_content}" awk -v pat="$pattern" -v block="$insert_content" ' { print if ($0 ~ pat && inserted != 1) { print block inserted=1 } } END { if (inserted == 1) exit 0 else exit 100 } ' "$file" >"$tmp" rc=$? if [[ $rc -eq 0 ]]; then mv "$tmp" "$file" echo "插入成功!" return 0 else rm -f "$tmp" echo "插入失败没找到" return 76 fi } _collect_files() { local path_list="$1" local exclude_list="$2" local -n out_files="$3" local -n out_skipped="$4" local paths=() local excludes=() local path="" local file="" local ex="" out_files=() out_skipped=() IFS=',' read -ra paths <<< "$path_list" IFS=',' read -ra excludes <<< "$exclude_list" for path in "${paths[@]}"; do [[ -z "$path" ]] && continue if [[ -f "$path" ]]; then local skip=0 for ex in "${excludes[@]}"; do [[ -z "$ex" ]] && continue if [[ "$path" == "$ex" ]] || \ [[ "$(basename "$path")" == "$ex" ]] || \ [[ "$path" == $ex ]]; then skip=1 break fi if [[ -d "$ex" ]]; then [[ "$path" == "$ex" || "$path" == "$ex"/* ]] && skip=1 && break fi done if [[ $skip -eq 1 ]]; then out_skipped+=("$path") else out_files+=("$path") fi elif [[ -d "$path" ]]; then while IFS= read -r -d '' file; do local skip=0 for ex in "${excludes[@]}"; do [[ -z "$ex" ]] && continue if [[ "$file" == "$ex" ]] || \ [[ "$(basename "$file")" == "$ex" ]] || \ [[ "$file" == $ex ]]; then skip=1 break fi if [[ -d "$ex" ]]; then [[ "$file" == "$ex" || "$file" == "$ex"/* ]] && skip=1 && break fi done if [[ $skip -eq 1 ]]; then out_skipped+=("$file") else out_files+=("$file") fi done < <(find "$path" -type f -print0) else echo "WARNING: path not found, skip: $path" fi done } sed_in_tree() { local path_list="$1" local search_str="$2" local replace_str="$3" local exclude_list="$4" local files=() local skipped=() _collect_files "$path_list" "$exclude_list" files skipped [[ -z "$search_str" ]] && { log_error "search_str is empty" return 1 } # ================= sed 替换规则说明 ================= # sed 替换基本格式: # s<分隔符>pattern<分隔符>replacement<分隔符>flags # -------------------------------------------------- # 一、分隔符(delimiter) # # - 可以自定义为任意“单个字符” # - 常见:/ | # @ # - 如果分隔符出现在 pattern 或 replacement 中,必须转义 # # 示例: # s|/home/user|/tmp|g # 推荐(避免转义 /) # # -------------------------------------------------- # 二、pattern(左边:正则表达式) # # 默认是 BRE(基本正则) # # 特殊字符: # . 任意字符 # * 重复 # ^ 行首 # $ 行尾 # [ ] 字符集合 # \ 转义符 # # 如果要匹配字面值,需要转义: # a.b -> a\.b # # -------------------------------------------------- # 三、replacement(右边:替换内容) # # 特殊符号: # & 代表“整个匹配内容” # \1 第1个分组 # \2 第2个分组 # \ 转义符 # # 示例: # s/b/&X/ -> b 变成 bX # # 如果要替换成字面 &: # \& # # -------------------------------------------------- # 四、分隔符冲突 # # 如果你用: # s|xxx|yyy| # # 那么: # | 出现在内容里必须写成 \| # # -------------------------------------------------- # 五、shell 层注意(非常重要) # # 在 bash 中: # sed "s|$search|$replace|g" # # 实际有三层解析: # 1. shell 变量展开 # 2. sed pattern(正则) # 3. sed replacement(特殊符号) # # 风险: # - pattern 被当正则(. * 等会生效) # - replacement 中 & 会被替换 # - 分隔符冲突导致报错 # # -------------------------------------------------- # 六、结论 # # - sed 转义符是:\ # - 左边是正则,右边是替换语法 # - 分隔符可以自定义,但要避免冲突 # # ================================================== printf '%s\0' "${files[@]}" | xargs -0 sed -i "s|${search_str}|${replace_str}|g" log_debug "-----------------------------------" log_debug "【$search_str】 替换为 【$replace_str】" log_debug "Files : ${#files[@]}" log_debug "Files skipped : ${#skipped[@]}" } #gen_pth my.pth \ # "D:\wyc\env\...\python3.12" \ # "D:\wyc\env\...\lib-dynload" gen_python_pth() { local pth_file="$1" shift if [ -z "$pth_file" ] || [ "$#" -eq 0 ]; then log_warn "Usage: gen_python_pth [path2 ...]" >&2 return 1 fi local pth_dir pth_dir="$(cd "$(dirname "$pth_file")" 2>/dev/null && pwd)" if [ -z "$pth_dir" ]; then log_error "Error: cannot resolve pth file directory: $pth_file" >&2 return 1 fi local p resolved # 先检查,全部通过后再写文件,避免生成半截内容 for p in "$@"; do case "$p" in /*|[A-Za-z]:[\\/]*) # 绝对路径:Linux /xxx 或 Windows C:\xxx / C:/xxx resolved="$p" ;; *) # 相对路径:相对于 .pth 文件所在目录 resolved="$pth_dir/$p" ;; esac if [ ! -d "$resolved" ]; then log_error "Error: directory does not exist: $p" >&2 log_error " resolved path: $resolved" >&2 return 1 fi done : > "$pth_file" || { log_error "Error: cannot write to $pth_file" >&2 return 1 } for p in "$@"; do echo "$p" >> "$pth_file" done log_debug "Generated $pth_file" } # /ae/bash/gcc/env/win_arm_kylin_930/sysroot/usr/lib/aarch64-linux-gnu/cmake # sysroot/usr/lib/aarch64-linux-gnu/cmake patch_system_qt5(){ local SYSROOT="${1}" local CMAKE_DIR="${2}" local qt_host_tool_dir="${3}" check_directory_var qt_host_tool_dir check_file_ex "传入的qt_host_tool_dir不对,不包含必须文件moc.exe" "${qt_host_tool_dir}/moc.exe" local dest="${SYSROOT}/usr/lib/qt5/bin" check_and_create_dir_var dest cp -r "${qt_host_tool_dir}/"* "${dest}" check_directory "${SYSROOT}" check_directory "${CMAKE_DIR}" local CMAKE_CURRENT_LIST_DIR="${CMAKE_DIR}/Qt5" check_directory "${CMAKE_CURRENT_LIST_DIR}" local exclude_list="${CMAKE_CURRENT_LIST_DIR}/Qt5Config.cmake" local Qt5WidgetsConfigExtras="${CMAKE_DIR}/Qt5Widgets/Qt5WidgetsConfigExtras.cmake" check_file_var Qt5WidgetsConfigExtras sed_in_tree \ "${Qt5WidgetsConfigExtras}" \ 'bin/uic"' \ 'bin/uic.exe"' \ "" local Qt5CoreConfigExtras="${CMAKE_DIR}/Qt5Core/Qt5CoreConfigExtras.cmake" check_file_var Qt5CoreConfigExtras sed_in_tree \ "${Qt5CoreConfigExtras}" \ 'bin/rcc"' \ 'bin/rcc.exe"' \ "" sed_in_tree \ "${Qt5CoreConfigExtras}" \ 'bin/moc"' \ 'bin/moc.exe"' \ "" # # sed_in_tree \ # "${Qt5CoreConfigExtras}" \ # 'bin/qmake"' \ # 'bin/qmake.exe"' \ # "" local QT5_DIR_LIST="" for d in "$CMAKE_DIR"/Qt5*; do [[ -d "$d" ]] || continue QT5_DIR_LIST+="${d}," done QT5_DIR_LIST="${QT5_DIR_LIST%,}" sed_in_tree \ "${QT5_DIR_LIST}" \ '"/usr' \ '"${_psc_sys_root}/usr' \ "${exclude_list}" local rel_path=$(realpath --relative-to="${CMAKE_CURRENT_LIST_DIR}" "${SYSROOT}") insert_after_match \ "${CMAKE_DIR}/Qt5/Qt5Config.cmake" \ 'get_filename_component[(]_qt5_install_prefix.*[)]' \ " if(CMAKE_SYSROOT) set(_psc_sys_root \"\${CMAKE_SYSROOT}\") message(STATUS \"CMAKE_SYSROOT:psc_sys_root = \${_psc_sys_root} at \${CMAKE_CURRENT_LIST_FILE} \") else() set(_psc_sys_root \${CMAKE_CURRENT_LIST_DIR}/${rel_path}) message(STATUS \"psc_sys_root = \${_psc_sys_root} at \${CMAKE_CURRENT_LIST_FILE} \") endif() " log_info "patch_system_qt end!" }