glance.bash
· 5.7 KiB · Bash
Sin formato
# 函数:glance - 快速概览目录内容
#
# 作者: Gemini & anduin
# 功能:
# - 自动检测 Git 仓库,智能选择文件列表模式。
# - 动态计算并调整 tree 的显示深度。
# - 文件内容输出严格遵循 tree 的显示深度。
# - 限制单个文件的最大输出行数。
# - 支持按文件名模式筛选文件。
#
# 用法:
# glance [目录] [选项...]
#
# 选项:
# --max-files-preview <数量> : tree 显示的最大条目数 (默认: 100)
# --max-file-lines <数量> : 单个文件的最大显示行数 (默认: 150)
# --filter <模式> : 按扩展名筛选,逗号分隔 (例: '*.md,*.cs')
#
glance() {
local target_dir="."
local max_items_preview=100
local max_file_lines=150
local filter_patterns=""
local positional_args=()
# --- 内部辅助函数:处理单个文件的打印 (无改动) ---
_glance_process_file() {
local file="$1"
if [ ! -f "$file" ]; then return; fi
if file -b --mime-type "$file" | grep -q '^text/'; then
local total_lines; total_lines=$(wc -l < "$file")
local extension="${file##*.}"; local lang=""
case "$extension" in
js) lang="javascript" ;; py) lang="python" ;; rb) lang="ruby" ;;
sh) lang="bash" ;; css) lang="css" ;; html) lang="html" ;;
json) lang="json" ;; md) lang="markdown" ;; yml|yaml) lang="yaml" ;;
java) lang="java" ;; c) lang="c" ;; cpp) lang="cpp" ;; go) lang="go" ;;
*) lang="" ;;
esac
echo; echo "Content of file \"$file\":"; echo "\`\`\`$lang"
if [ "$total_lines" -gt "$max_file_lines" ]; then
head -n "$max_file_lines" "$file"; echo; echo "... (Only shown top $max_file_lines lines of $total_lines) ..."
else
cat "$file"
fi
echo; echo "\`\`\`"; echo "----------------------------------------------------------"
else
echo; echo "--> 已跳过二进制文件: \"$file\""; echo "----------------------------------------------------------"
fi
}
# --- 步骤 1: 解析参数 ---
while [[ $# -gt 0 ]]; do
case "$1" in
--max-files-preview)
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then max_items_preview="$2"; shift 2;
else echo "错误: '--max-files-preview' 需要一个正整数参数。" >&2; return 1; fi ;;
--max-file-lines)
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then max_file_lines="$2"; shift 2;
else echo "错误: '--max-file-lines' 需要一个正整数参数。" >&2; return 1; fi ;;
--filter)
if [[ -n "$2" && "$2" != -* ]]; then filter_patterns="$2"; shift 2;
else echo "错误: '--filter' 需要一个模式字符串参数。" >&2; return 1; fi ;;
-*) echo "错误: 未知选项 '$1'" >&2; return 1 ;;
*) positional_args+=("$1"); shift ;;
esac
done
[[ ${#positional_args[@]} -gt 0 ]] && target_dir="${positional_args[0]}"
# --- 步骤 2: 依赖和目录检查 ---
if ! command -v tree &>/dev/null; then echo "错误: 'tree' 命令未找到。" >&2; return 1; fi
if [ ! -d "$target_dir" ]; then echo "错误: 目录 '$target_dir' 不存在。" >&2; return 1; fi
echo "=========================================================="
echo "🔎 概览目录: $(realpath "$target_dir")"
echo "=========================================================="
echo
# --- 步骤 3: 动态计算 tree 的最佳深度 (基于未筛选的完整目录) ---
echo "🌳 目录结构:"
local best_depth=1; local max_depth_to_check=6; local prev_item_count=-1
for depth in $(seq 1 $max_depth_to_check); do
local current_item_count; current_item_count=$(tree -L "$depth" -a --noreport "$target_dir" | wc -l)
if [ "$depth" -gt 1 ] && [ "$current_item_count" -gt "$max_items_preview" ]; then
best_depth=$((depth - 1)); break;
fi
if [ "$depth" -gt 1 ] && [ "$current_item_count" -eq "$prev_item_count" ]; then
best_depth=$depth; break;
fi
best_depth=$depth; prev_item_count=$current_item_count
done
# --- 步骤 3.5: 应用筛选并打印 tree ---
local tree_args=(-L "$best_depth")
if [ -n "$filter_patterns" ]; then
local tree_pattern; tree_pattern=$(echo "$filter_patterns" | tr ',' '|')
tree_args+=(-P "$tree_pattern")
echo "(筛选模式: ${filter_patterns})"
fi
echo "(动态调整深度至 L${best_depth} 以适应预览数限制: ${max_items_preview})"
tree "${tree_args[@]}" "$target_dir"
echo
# --- 步骤 4: 遍历并打印文件内容 (应用筛选和深度限制) ---
local header_suffix=""
if [ -n "$filter_patterns" ]; then header_suffix=" (筛选: ${filter_patterns})"; fi
echo "📄 文件内容详情 (深度 L${best_depth} 以内, 单文件最多 ${max_file_lines} 行${header_suffix}):"
(
cd "$target_dir" || exit 1
echo "----------------------------------------------------------"
local filter_array=()
[ -n "$filter_patterns" ] && IFS=',' read -ra filter_array <<< "$filter_patterns"
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
# Git 模式
local git_files
if [ ${#filter_array[@]} -gt 0 ]; then
git_files=$(git ls-files -- "${filter_array[@]}")
else
git_files=$(git ls-files)
fi
echo "$git_files" | awk -F'/' -v depth="$best_depth" 'NF <= depth' | while read -r file; do
_glance_process_file "$file"
done
else
# 普通模式
local find_args=(-maxdepth "$best_depth" -type f)
if [ ${#filter_array[@]} -gt 0 ]; then
find_args+=("(")
for i in "${!filter_array[@]}"; do
[ "$i" -ne 0 ] && find_args+=("-o")
find_args+=(-name "${filter_array[$i]}")
done
find_args+=(")")
fi
find . "${find_args[@]}" | while read -r file; do
_glance_process_file "${file#./}"
done
fi
)
}
1 | # 函数:glance - 快速概览目录内容 |
2 | # |
3 | # 作者: Gemini & anduin |
4 | # 功能: |
5 | # - 自动检测 Git 仓库,智能选择文件列表模式。 |
6 | # - 动态计算并调整 tree 的显示深度。 |
7 | # - 文件内容输出严格遵循 tree 的显示深度。 |
8 | # - 限制单个文件的最大输出行数。 |
9 | # - 支持按文件名模式筛选文件。 |
10 | # |
11 | # 用法: |
12 | # glance [目录] [选项...] |
13 | # |
14 | # 选项: |
15 | # --max-files-preview <数量> : tree 显示的最大条目数 (默认: 100) |
16 | # --max-file-lines <数量> : 单个文件的最大显示行数 (默认: 150) |
17 | # --filter <模式> : 按扩展名筛选,逗号分隔 (例: '*.md,*.cs') |
18 | # |
19 | glance() { |
20 | local target_dir="." |
21 | local max_items_preview=100 |
22 | local max_file_lines=150 |
23 | local filter_patterns="" |
24 | local positional_args=() |
25 | |
26 | # --- 内部辅助函数:处理单个文件的打印 (无改动) --- |
27 | _glance_process_file() { |
28 | local file="$1" |
29 | if [ ! -f "$file" ]; then return; fi |
30 | if file -b --mime-type "$file" | grep -q '^text/'; then |
31 | local total_lines; total_lines=$(wc -l < "$file") |
32 | local extension="${file##*.}"; local lang="" |
33 | case "$extension" in |
34 | js) lang="javascript" ;; py) lang="python" ;; rb) lang="ruby" ;; |
35 | sh) lang="bash" ;; css) lang="css" ;; html) lang="html" ;; |
36 | json) lang="json" ;; md) lang="markdown" ;; yml|yaml) lang="yaml" ;; |
37 | java) lang="java" ;; c) lang="c" ;; cpp) lang="cpp" ;; go) lang="go" ;; |
38 | *) lang="" ;; |
39 | esac |
40 | echo; echo "Content of file \"$file\":"; echo "\`\`\`$lang" |
41 | if [ "$total_lines" -gt "$max_file_lines" ]; then |
42 | head -n "$max_file_lines" "$file"; echo; echo "... (Only shown top $max_file_lines lines of $total_lines) ..." |
43 | else |
44 | cat "$file" |
45 | fi |
46 | echo; echo "\`\`\`"; echo "----------------------------------------------------------" |
47 | else |
48 | echo; echo "--> 已跳过二进制文件: \"$file\""; echo "----------------------------------------------------------" |
49 | fi |
50 | } |
51 | |
52 | # --- 步骤 1: 解析参数 --- |
53 | while [[ $# -gt 0 ]]; do |
54 | case "$1" in |
55 | --max-files-preview) |
56 | if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then max_items_preview="$2"; shift 2; |
57 | else echo "错误: '--max-files-preview' 需要一个正整数参数。" >&2; return 1; fi ;; |
58 | --max-file-lines) |
59 | if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then max_file_lines="$2"; shift 2; |
60 | else echo "错误: '--max-file-lines' 需要一个正整数参数。" >&2; return 1; fi ;; |
61 | --filter) |
62 | if [[ -n "$2" && "$2" != -* ]]; then filter_patterns="$2"; shift 2; |
63 | else echo "错误: '--filter' 需要一个模式字符串参数。" >&2; return 1; fi ;; |
64 | -*) echo "错误: 未知选项 '$1'" >&2; return 1 ;; |
65 | *) positional_args+=("$1"); shift ;; |
66 | esac |
67 | done |
68 | [[ ${#positional_args[@]} -gt 0 ]] && target_dir="${positional_args[0]}" |
69 | |
70 | # --- 步骤 2: 依赖和目录检查 --- |
71 | if ! command -v tree &>/dev/null; then echo "错误: 'tree' 命令未找到。" >&2; return 1; fi |
72 | if [ ! -d "$target_dir" ]; then echo "错误: 目录 '$target_dir' 不存在。" >&2; return 1; fi |
73 | |
74 | echo "==========================================================" |
75 | echo "🔎 概览目录: $(realpath "$target_dir")" |
76 | echo "==========================================================" |
77 | echo |
78 | |
79 | # --- 步骤 3: 动态计算 tree 的最佳深度 (基于未筛选的完整目录) --- |
80 | echo "🌳 目录结构:" |
81 | local best_depth=1; local max_depth_to_check=6; local prev_item_count=-1 |
82 | for depth in $(seq 1 $max_depth_to_check); do |
83 | local current_item_count; current_item_count=$(tree -L "$depth" -a --noreport "$target_dir" | wc -l) |
84 | if [ "$depth" -gt 1 ] && [ "$current_item_count" -gt "$max_items_preview" ]; then |
85 | best_depth=$((depth - 1)); break; |
86 | fi |
87 | if [ "$depth" -gt 1 ] && [ "$current_item_count" -eq "$prev_item_count" ]; then |
88 | best_depth=$depth; break; |
89 | fi |
90 | best_depth=$depth; prev_item_count=$current_item_count |
91 | done |
92 | |
93 | # --- 步骤 3.5: 应用筛选并打印 tree --- |
94 | local tree_args=(-L "$best_depth") |
95 | if [ -n "$filter_patterns" ]; then |
96 | local tree_pattern; tree_pattern=$(echo "$filter_patterns" | tr ',' '|') |
97 | tree_args+=(-P "$tree_pattern") |
98 | echo "(筛选模式: ${filter_patterns})" |
99 | fi |
100 | echo "(动态调整深度至 L${best_depth} 以适应预览数限制: ${max_items_preview})" |
101 | tree "${tree_args[@]}" "$target_dir" |
102 | echo |
103 | |
104 | # --- 步骤 4: 遍历并打印文件内容 (应用筛选和深度限制) --- |
105 | local header_suffix="" |
106 | if [ -n "$filter_patterns" ]; then header_suffix=" (筛选: ${filter_patterns})"; fi |
107 | echo "📄 文件内容详情 (深度 L${best_depth} 以内, 单文件最多 ${max_file_lines} 行${header_suffix}):" |
108 | ( |
109 | cd "$target_dir" || exit 1 |
110 | echo "----------------------------------------------------------" |
111 | |
112 | local filter_array=() |
113 | [ -n "$filter_patterns" ] && IFS=',' read -ra filter_array <<< "$filter_patterns" |
114 | |
115 | if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then |
116 | # Git 模式 |
117 | local git_files |
118 | if [ ${#filter_array[@]} -gt 0 ]; then |
119 | git_files=$(git ls-files -- "${filter_array[@]}") |
120 | else |
121 | git_files=$(git ls-files) |
122 | fi |
123 | echo "$git_files" | awk -F'/' -v depth="$best_depth" 'NF <= depth' | while read -r file; do |
124 | _glance_process_file "$file" |
125 | done |
126 | else |
127 | # 普通模式 |
128 | local find_args=(-maxdepth "$best_depth" -type f) |
129 | if [ ${#filter_array[@]} -gt 0 ]; then |
130 | find_args+=("(") |
131 | for i in "${!filter_array[@]}"; do |
132 | [ "$i" -ne 0 ] && find_args+=("-o") |
133 | find_args+=(-name "${filter_array[$i]}") |
134 | done |
135 | find_args+=(")") |
136 | fi |
137 | find . "${find_args[@]}" | while read -r file; do |
138 | _glance_process_file "${file#./}" |
139 | done |
140 | fi |
141 | ) |
142 | } |