Skip to content

Instantly share code, notes, and snippets.

@kuyagic
Created February 1, 2026 10:48
Show Gist options
  • Select an option

  • Save kuyagic/b573c3cfa7f038ecc51006e01a340300 to your computer and use it in GitHub Desktop.

Select an option

Save kuyagic/b573c3cfa7f038ecc51006e01a340300 to your computer and use it in GitHub Desktop.
Generate the file list based on the ignore configuration and CACHEDIR.TAG.
fdfile() {
local output_file=""
local OPTIND opt
# === 选项解析:仅支持 -o output_file ===
while getopts "o:" opt; do
case "$opt" in
o) output_file="$OPTARG" ;;
*)
echo "Usage: fdfile [-o OUTPUT_FILE] DIR1 [DIR2 ...]" >&2
echo " -o FILE Write results to FILE instead of stdout" >&2
return 1
;;
esac
done
shift $((OPTIND - 1))
# === 参数校验:至少需要一个目录 ===
if [[ $# -eq 0 ]]; then
echo "❌ Error: At least one directory argument required" >&2
echo "Usage: fdfile [-o OUTPUT_FILE] DIR1 [DIR2 ...]" >&2
return 1
fi
# === 创建全局临时目录(用于 EXIT trap 清理) ===
_FDFILE_TEMP_DIR=$(mktemp -d) || {
echo "❌ mktemp failed" >&2
return 1
}
cleanup() {
[[ -n "${_FDFILE_TEMP_DIR:-}" ]] && rm -rf "$_FDFILE_TEMP_DIR"
}
trap cleanup EXIT
# === 保留文件清单(用于缓存目录内白名单) ===
local KEEP_IN_CACHE_DIR=(
"CACHEDIR.TAG"
".ignore"
".gitignore"
".fdignore"
".rgignore"
".kopiaignore"
)
printf '%s\n' "${KEEP_IN_CACHE_DIR[@]}" > "$_FDFILE_TEMP_DIR/keep_in_cache.txt"
local RESULT="$_FDFILE_TEMP_DIR/files.txt"
> "$RESULT"
# === 遍历所有目录参数 ===
local ROOT
for ROOT in "$@"; do
[[ -z "$ROOT" ]] && continue
ROOT="$(realpath "$ROOT" 2>/dev/null)" || {
echo "⚠️ Invalid path: $ROOT" >&2
continue
}
[[ -d "$ROOT" ]] || {
echo "⚠️ Skip non-dir: $ROOT" >&2
continue
}
echo "🔎 Scanning $ROOT" >&2
# 生成唯一临时文件名(基于目录路径哈希)
local HASH
HASH=$(printf '%s' "$ROOT" | sha1sum 2>/dev/null | cut -c1-8 || echo "default")
local FD_LIST="$_FDFILE_TEMP_DIR/fd_${HASH}.txt"
local CACHE_DIRS="$_FDFILE_TEMP_DIR/cache_dirs_${HASH}.txt"
# Step 1: 使用 fdfind 获取文件列表(尊重 ignore 规则)
(cd "$ROOT" && fdfind --type f --hidden --strip-cwd-prefix 2>/dev/null) > "$FD_LIST" || {
echo "⚠️ fd failed in $ROOT" >&2
continue
}
# Step 2: 查找所有包含 CACHEDIR.TAG 的缓存目录
find "$ROOT" -name CACHEDIR.TAG -exec dirname {} \; 2>/dev/null | sed 's|/*$|/|' > "$CACHE_DIRS"
# Step 3: 过滤逻辑
local relpath abspath basename cache_dir in_cache_dir
while IFS= read -r relpath || [[ -n "$relpath" ]]; do
[[ -z "$relpath" ]] && continue
abspath="$ROOT/$relpath"
basename="${relpath##*/}"
in_cache_dir=false
while IFS= read -r cache_dir || [[ -n "$cache_dir" ]]; do
[[ -z "$cache_dir" ]] && continue
[[ "$abspath/" == "$cache_dir"* ]] && { in_cache_dir=true; break; }
done < "$CACHE_DIRS"
if $in_cache_dir; then
grep -Fxq "$basename" "$_FDFILE_TEMP_DIR/keep_in_cache.txt" 2>/dev/null && echo "$abspath"
else
echo "$abspath"
fi
done < "$FD_LIST" >> "$RESULT"
done
# === 输出结果:根据 -o 参数决定输出目标 ===
if [[ -n "$output_file" ]]; then
if ! sort -u "$RESULT" > "$output_file"; then
echo "❌ Failed to write output to: $output_file" >&2
return 1
fi
echo "✅ Results written to: $output_file" >&2
else
sort -u "$RESULT"
fi
}
# === 新增:如果脚本被直接执行,则调用函数 ===
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
# 被直接执行(而非被 source)
fdfile "$@"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment