250 lines
6.6 KiB
Bash
Executable File
250 lines
6.6 KiB
Bash
Executable File
#!/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 "$@" |