#!/usr/bin/env bash # ============================================================================ # SCRIPT : reuse_uid_to_ogg.sh (v4.0 - 增强版) # TASK : 将 *.wav.import / *.mp3.import 的 UID 迁移到对应 *.ogg.import # 并删除源文件中的 UID 行 # USAGE : DEBUG=1 ./reuse_uid_to_ogg.sh # 调试模式 # DRY_RUN=1 ./reuse_uid_to_ogg.sh # 演练 # ./reuse_uid_to_ogg.sh # 实际写入 # ============================================================================ set -Euo pipefail [[ ${DEBUG:-0} == 1 ]] && set -x ROOT_DIR="${ROOT_DIR:-asset/audio}" DRY_RUN="${DRY_RUN:-0}" VERBOSE="${VERBOSE:-0}" MODIFIED=0 SKIPPED=0 ERRORS=0 # ---------- 错误处理 ---------- error_handler() { local line_no=$1 local error_code=$2 printf "❌ 错误发生在第 %s 行,错误代码: %s\n" "$line_no" "$error_code" >&2 printf " 最后处理的文件: %s\n" "${CURRENT_FILE:-未知}" >&2 exit "$error_code" } trap 'error_handler ${LINENO} $?' ERR # ---------- 工具函数 ---------- log() { [[ $VERBOSE == 1 ]] && echo "[LOG] $*" >&2 } check_writable() { local f=$1 if [[ ! -f "$f" ]]; then printf "❌ 文件不存在:%s\n" "$f" >&2 return 1 fi if [[ ! -w "$f" ]]; then printf "❌ 无写权限:%s\n" "$f" >&2 return 1 fi return 0 } get_uid() { local file=$1 local uid="" if [[ ! -f "$file" ]]; then log "文件不存在: $file" return 1 fi # 提取 UID,处理可能的空格和回车 uid=$(awk -F'"' '/^[[:space:]]*uid="uid:\/\// {print $2; exit}' "$file" 2>/dev/null | tr -d '\r\n' || true) if [[ -n "$uid" ]]; then echo "$uid" return 0 else return 1 fi } # ---------- 把 UID 写进 .ogg.import ---------- put_uid() { local file=$1 local uid=$2 if ! check_writable "$file"; then ((ERRORS++)) return 1 fi # 统一转义处理 local esc=${uid//\\/\\\\} # 反斜线 esc=${esc//&/\\&} # & esc=${esc//@/\\@} # @ esc=${esc//\//\\/} # / if [[ $DRY_RUN == 1 ]]; then printf " ↪ [演练] 更新 %-30s → %s\n" "$(basename "$file")" "$uid" return 0 fi local tmp if ! tmp=$(mktemp); then printf "❌ 无法创建临时文件\n" >&2 ((ERRORS++)) return 1 fi # 确保临时文件被清理 trap "rm -f '$tmp'" RETURN local success=0 if grep -qE '^[[:space:]]*uid="uid://[^"]+"' "$file" 2>/dev/null; then # 已有 UID → 替换 if sed -E "s@^[[:space:]]*uid=\"uid://[^\"]+\"@uid=\"${esc}\"@" "$file" >"$tmp" 2>/dev/null; then success=1 fi else # 无 UID → 插入 if grep -q '^\[remap\]' "$file" 2>/dev/null; then if sed -E "/^\[remap\]/a\\ uid=\"${esc}\" " "$file" >"$tmp" 2>/dev/null; then success=1 fi else if cat "$file" >"$tmp" 2>/dev/null && printf 'uid="%s"\n' "$uid" >>"$tmp" 2>/dev/null; then success=1 fi fi fi if [[ $success == 1 ]] && mv "$tmp" "$file" 2>/dev/null; then ((MODIFIED++)) log "成功更新 $file" return 0 else printf "❌ 更新失败:%s\n" "$file" >&2 ((ERRORS++)) return 1 fi } # ---------- 删除源文件的 UID 行 ---------- delete_uid_line() { local file=$1 if ! check_writable "$file"; then ((ERRORS++)) return 1 fi if [[ $DRY_RUN == 1 ]]; then printf " ↪ [演练] 删除 %-30s 中的 UID 行\n" "$(basename "$file")" return 0 fi local tmp if ! tmp=$(mktemp); then printf "❌ 无法创建临时文件\n" >&2 ((ERRORS++)) return 1 fi # 确保临时文件被清理 trap "rm -f '$tmp'" RETURN if sed -E '/^[[:space:]]*uid="uid:\/\/[^"]+"/d' "$file" >"$tmp" 2>/dev/null && \ mv "$tmp" "$file" 2>/dev/null; then ((MODIFIED++)) log "成功删除 $file 中的 UID" return 0 else printf "❌ 删除 UID 失败:%s\n" "$file" >&2 ((ERRORS++)) return 1 fi } # ---------- 主程序 ---------- main() { printf "=== 开始 UID 迁移 (DRY_RUN=%s, ROOT_DIR=%s) ===\n" "$DRY_RUN" "$ROOT_DIR" # 检查根目录 if [[ ! -d "$ROOT_DIR" ]]; then printf "❌ 目录不存在:%s\n" "$ROOT_DIR" >&2 exit 1 fi # 使用数组存储文件,避免管道子shell问题 local files=() while IFS= read -r -d '' file; do files+=("$file") done < <(find "$ROOT_DIR" -type f \( -name '*.wav.import' -o -name '*.mp3.import' \) -print0 2>/dev/null || true) if [[ ${#files[@]} -eq 0 ]]; then printf "未找到任何 .wav.import 或 .mp3.import 文件\n" exit 0 fi printf "找到 %d 个源文件\n\n" "${#files[@]}" # 处理每个文件 for src in "${files[@]}"; do CURRENT_FILE="$src" # 获取 UID local src_uid if ! src_uid=$(get_uid "$src"); then printf "🟡 跳过 (无 UID) : %s\n" "$(basename "$src")" ((SKIPPED++)) continue fi # 构建对应的 .ogg.import 文件路径 local base="${src%.*.*}" local ogg="$base.ogg.import" # 检查 .ogg.import 是否存在 if [[ ! -f "$ogg" ]]; then printf "🟡 跳过 (无 OGG) : %s\n" "$(basename "$src")" ((SKIPPED++)) continue fi # 检查 UID 是否已同步 local ogg_uid ogg_uid=$(get_uid "$ogg" 2>/dev/null || echo "") if [[ "$src_uid" == "$ogg_uid" ]]; then printf "🟢 跳过 (UID 已同步) : %s\n" "$(basename "$src")" ((SKIPPED++)) continue fi # 处理文件 printf "🔄 处理 : %s → %s\n" "$(basename "$src")" "$(basename "$ogg")" if put_uid "$ogg" "$src_uid"; then delete_uid_line "$src" || true # 即使删除失败也继续 fi done # 输出统计 printf "\n=== 处理完毕 ===\n" if [[ $DRY_RUN == 1 ]]; then printf "演练模式,未实际修改任何文件。\n" else printf "✅ 成功处理: %d 个文件\n" "$((MODIFIED/2))" printf "🟡 跳过: %d 个文件\n" "$SKIPPED" if [[ $ERRORS -gt 0 ]]; then printf "❌ 错误: %d 个\n" "$ERRORS" fi fi return 0 } # 运行主程序 main "$@"