- CLI 定义迁移至 src/bashly.yml(bashly 1.4 生成参数解析),入口收敛为 src/root_command.sh - 源码按功能域重组:config/storage/integrate/media/pipeline,消除扁平散落 - 新增零依赖测试框架 tests/(123 用例)与 CI 流水线(lint + test + build + 产物一致性) - build.sh 支持版本单一来源注入与 --check 产物校验;新增 lint.sh(bash -n + shellcheck 零容忍) - 删除旧单文件 media_organizer.sh 与旧扁平模块
91 lines
2.6 KiB
Bash
91 lines
2.6 KiB
Bash
# shellcheck shell=bash
|
||
|
||
################################################################################
|
||
# 硬链接处理
|
||
################################################################################
|
||
|
||
# 判断源与目标是否为同一 inode(硬链接已存在)。
|
||
# 获取文件 inode(兼容 GNU/BSD/BusyBox stat)。
|
||
# 依次尝试:GNU stat -c '%i' -> BSD stat -f '%i' -> ls -i -> find -printf。
|
||
get_file_inode() {
|
||
local filepath="$1"
|
||
local inode
|
||
inode=$(stat -c '%i' "$filepath" 2>/dev/null)
|
||
[[ -n "$inode" ]] && {
|
||
echo "$inode"
|
||
return 0
|
||
}
|
||
inode=$(stat -f '%i' "$filepath" 2>/dev/null)
|
||
[[ -n "$inode" ]] && {
|
||
echo "$inode"
|
||
return 0
|
||
}
|
||
# ls -i 解析 inode 字段(BusyBox 可靠,stat/find -printf 可能不可用)
|
||
# shellcheck disable=SC2012
|
||
inode=$(ls -i "$filepath" 2>/dev/null | awk '{print $1}')
|
||
[[ -n "$inode" ]] && {
|
||
echo "$inode"
|
||
return 0
|
||
}
|
||
# GNU find 兜底:-printf 解析 inode
|
||
find "$filepath" -maxdepth 0 -printf '%i' 2>/dev/null
|
||
}
|
||
|
||
same_inode() {
|
||
local src="$1" dst="$2"
|
||
[[ ! -e "$dst" ]] && return 1
|
||
local src_inode dst_inode
|
||
|
||
src_inode=$(get_file_inode "$src")
|
||
dst_inode=$(get_file_inode "$dst")
|
||
[[ -z "$src_inode" || -z "$dst_inode" ]] && return 1
|
||
|
||
[[ "$src_inode" == "$dst_inode" ]]
|
||
}
|
||
|
||
# 创建硬链接;干运行模式下仅打印;目标已存在时直接替换(不备份)。
|
||
# 注:不做 .bak 备份——遗留的 .bak_* 文件会被 Jellyfin 当作媒体扫描,干扰刮削。
|
||
hardlink_or_dryrun() {
|
||
local src="$1" dst="$2"
|
||
|
||
if $DRY_RUN; then
|
||
_log 信息 "干运行:硬链接 '$src' -> '$dst'"
|
||
return 0
|
||
fi
|
||
|
||
if same_inode "$src" "$dst"; then
|
||
_log 跳过 "硬链接已存在,inode: $(stat -c '%i' "$src" 2>/dev/null)"
|
||
return 0
|
||
fi
|
||
|
||
# 目标已存在:直接替换(先删旧目标再建硬链接)
|
||
if [[ -e "$dst" ]]; then
|
||
# 安全保护:目标是目录(如误传根目录)时拒绝替换,避免误操作真实目录
|
||
if [[ -d "$dst" ]]; then
|
||
_log 错误 "目标是目录,拒绝替换: $dst"
|
||
return 1
|
||
fi
|
||
_log 信息 "目标已存在,直接替换: $dst"
|
||
rm -f "$dst"
|
||
fi
|
||
|
||
if ln "$src" "$dst"; then
|
||
_log 成功 "硬链接已创建"
|
||
return 0
|
||
else
|
||
_log 错误 "硬链接创建失败"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
################################################################################
|
||
# 错误处理
|
||
################################################################################
|
||
|
||
# 全局 ERR trap:记录出错行号后退出。
|
||
_handle_error() {
|
||
local line_num="$1"
|
||
_log 错误 "脚本在第 $line_num 行出错(错误码:$?)"
|
||
exit 1
|
||
}
|