anduin ревизій цього gist 2 months ago. До ревизії
1 file changed, 59 insertions, 39 deletions
glance.bash
| @@ -1,20 +1,21 @@ | |||
| 1 | - | # 函数:glance - 快速概览目录内容 | |
| 1 | + | # glance - Provides a quick and intelligent overview of a directory's contents. | |
| 2 | 2 | # | |
| 3 | - | # 作者: Gemini & anduin | |
| 4 | - | # 功能: | |
| 5 | - | # - 自动检测 Git 仓库,智能选择文件列表模式。 | |
| 6 | - | # - 动态计算并调整 tree 的显示深度。 | |
| 7 | - | # - 文件内容输出严格遵循 tree 的显示深度。 | |
| 8 | - | # - 限制单个文件的最大输出行数。 | |
| 9 | - | # - 支持按文件名模式筛选文件。 | |
| 3 | + | # Authors: anduin | |
| 10 | 4 | # | |
| 11 | - | # 用法: | |
| 12 | - | # glance [目录] [选项...] | |
| 5 | + | # Features: | |
| 6 | + | # - Auto-detects Git repositories to intelligently list files. | |
| 7 | + | # - Dynamically calculates and adjusts the `tree` view depth to prevent clutter. | |
| 8 | + | # - File content display strictly adheres to the calculated tree depth. | |
| 9 | + | # - Limits the maximum lines printed per file to avoid long scrolls. | |
| 10 | + | # - Supports powerful filtering by file glob patterns. | |
| 13 | 11 | # | |
| 14 | - | # 选项: | |
| 15 | - | # --max-files-preview <数量> : tree 显示的最大条目数 (默认: 100) | |
| 16 | - | # --max-file-lines <数量> : 单个文件的最大显示行数 (默认: 150) | |
| 17 | - | # --filter <模式> : 按扩展名筛选,逗号分隔 (例: '*.md,*.cs') | |
| 12 | + | # Usage: | |
| 13 | + | # glance [DIRECTORY] [OPTIONS...] | |
| 14 | + | # | |
| 15 | + | # Options: | |
| 16 | + | # --max-files-preview <count> : Max items (files+dirs) in tree view. (Default: 100) | |
| 17 | + | # --max-file-lines <count> : Max lines to print per file. (Default: 150) | |
| 18 | + | # --filter <patterns> : Comma-separated glob patterns to filter files (e.g., '*.md,*.cs') | |
| 18 | 19 | # | |
| 19 | 20 | glance() { | |
| 20 | 21 | local target_dir="." | |
| @@ -23,12 +24,16 @@ glance() { | |||
| 23 | 24 | local filter_patterns="" | |
| 24 | 25 | local positional_args=() | |
| 25 | 26 | ||
| 26 | - | # --- 内部辅助函数:处理单个文件的打印 (无改动) --- | |
| 27 | + | # --- Internal helper function to process and print a single file --- | |
| 27 | 28 | _glance_process_file() { | |
| 28 | 29 | local file="$1" | |
| 29 | 30 | if [ ! -f "$file" ]; then return; fi | |
| 31 | + | ||
| 32 | + | # Check if it's a text file | |
| 30 | 33 | if file -b --mime-type "$file" | grep -q '^text/'; then | |
| 31 | - | local total_lines; total_lines=$(wc -l < "$file") | |
| 34 | + | local total_lines | |
| 35 | + | total_lines=$(wc -l < "$file") # More efficient line count | |
| 36 | + | ||
| 32 | 37 | local extension="${file##*.}"; local lang="" | |
| 33 | 38 | case "$extension" in | |
| 34 | 39 | js) lang="javascript" ;; py) lang="python" ;; rb) lang="ruby" ;; | |
| @@ -37,74 +42,86 @@ glance() { | |||
| 37 | 42 | java) lang="java" ;; c) lang="c" ;; cpp) lang="cpp" ;; go) lang="go" ;; | |
| 38 | 43 | *) lang="" ;; | |
| 39 | 44 | esac | |
| 40 | - | echo; echo "Content of file \"$file\":"; echo "\`\`\`$lang" | |
| 45 | + | ||
| 46 | + | echo | |
| 47 | + | echo "Content of file \"$file\":" | |
| 48 | + | echo "\`\`\`$lang" | |
| 49 | + | ||
| 41 | 50 | 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) ..." | |
| 51 | + | head -n "$max_file_lines" "$file" | |
| 52 | + | echo | |
| 53 | + | echo "... (Only shown top $max_file_lines lines of $total_lines) ..." | |
| 43 | 54 | else | |
| 44 | 55 | cat "$file" | |
| 45 | 56 | fi | |
| 46 | - | echo; echo "\`\`\`"; echo "----------------------------------------------------------" | |
| 57 | + | ||
| 58 | + | echo | |
| 59 | + | echo "\`\`\`" | |
| 60 | + | echo "----------------------------------------------------------" | |
| 47 | 61 | else | |
| 48 | - | echo; echo "--> 已跳过二进制文件: \"$file\""; echo "----------------------------------------------------------" | |
| 62 | + | echo | |
| 63 | + | echo "--> Skipped binary file: \"$file\"" | |
| 64 | + | echo "----------------------------------------------------------" | |
| 49 | 65 | fi | |
| 50 | 66 | } | |
| 51 | 67 | ||
| 52 | - | # --- 步骤 1: 解析参数 --- | |
| 68 | + | # --- Step 1: Parse arguments --- | |
| 53 | 69 | while [[ $# -gt 0 ]]; do | |
| 54 | 70 | case "$1" in | |
| 55 | 71 | --max-files-preview) | |
| 56 | 72 | if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then max_items_preview="$2"; shift 2; | |
| 57 | - | else echo "错误: '--max-files-preview' 需要一个正整数参数。" >&2; return 1; fi ;; | |
| 73 | + | else echo "Error: '--max-files-preview' requires a positive integer argument." >&2; return 1; fi ;; | |
| 58 | 74 | --max-file-lines) | |
| 59 | 75 | if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then max_file_lines="$2"; shift 2; | |
| 60 | - | else echo "错误: '--max-file-lines' 需要一个正整数参数。" >&2; return 1; fi ;; | |
| 76 | + | else echo "Error: '--max-file-lines' requires a positive integer argument." >&2; return 1; fi ;; | |
| 61 | 77 | --filter) | |
| 62 | 78 | if [[ -n "$2" && "$2" != -* ]]; then filter_patterns="$2"; shift 2; | |
| 63 | - | else echo "错误: '--filter' 需要一个模式字符串参数。" >&2; return 1; fi ;; | |
| 64 | - | -*) echo "错误: 未知选项 '$1'" >&2; return 1 ;; | |
| 79 | + | else echo "Error: '--filter' requires a pattern string argument." >&2; return 1; fi ;; | |
| 80 | + | -*) echo "Error: Unknown option '$1'" >&2; return 1 ;; | |
| 65 | 81 | *) positional_args+=("$1"); shift ;; | |
| 66 | 82 | esac | |
| 67 | 83 | done | |
| 68 | 84 | [[ ${#positional_args[@]} -gt 0 ]] && target_dir="${positional_args[0]}" | |
| 69 | 85 | ||
| 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 | |
| 86 | + | # --- Step 2: Check dependencies and directory validity --- | |
| 87 | + | if ! command -v tree &>/dev/null; then echo "Error: 'tree' command not found. Please install it first." >&2; return 1; fi | |
| 88 | + | if [ ! -d "$target_dir" ]; then echo "Error: Directory '$target_dir' does not exist." >&2; return 1; fi | |
| 73 | 89 | ||
| 74 | 90 | echo "==========================================================" | |
| 75 | - | echo "🔎 概览目录: $(realpath "$target_dir")" | |
| 91 | + | echo "🔎 Overview for directory: $(realpath "$target_dir")" | |
| 76 | 92 | echo "==========================================================" | |
| 77 | 93 | echo | |
| 78 | 94 | ||
| 79 | - | # --- 步骤 3: 动态计算 tree 的最佳深度 (基于未筛选的完整目录) --- | |
| 80 | - | echo "🌳 目录结构:" | |
| 95 | + | # --- Step 3: Dynamically calculate the optimal tree depth (based on the full, unfiltered directory) --- | |
| 96 | + | echo "🌳 Directory Structure:" | |
| 81 | 97 | local best_depth=1; local max_depth_to_check=6; local prev_item_count=-1 | |
| 82 | 98 | for depth in $(seq 1 $max_depth_to_check); do | |
| 83 | 99 | local current_item_count; current_item_count=$(tree -L "$depth" -a --noreport "$target_dir" | wc -l) | |
| 84 | 100 | if [ "$depth" -gt 1 ] && [ "$current_item_count" -gt "$max_items_preview" ]; then | |
| 85 | 101 | best_depth=$((depth - 1)); break; | |
| 86 | 102 | fi | |
| 103 | + | # If item count stops increasing, we've reached the maximum effective depth | |
| 87 | 104 | if [ "$depth" -gt 1 ] && [ "$current_item_count" -eq "$prev_item_count" ]; then | |
| 88 | 105 | best_depth=$depth; break; | |
| 89 | 106 | fi | |
| 90 | 107 | best_depth=$depth; prev_item_count=$current_item_count | |
| 91 | 108 | done | |
| 92 | 109 | ||
| 93 | - | # --- 步骤 3.5: 应用筛选并打印 tree --- | |
| 110 | + | # --- Step 3.5: Apply filter and print the tree --- | |
| 94 | 111 | local tree_args=(-L "$best_depth") | |
| 95 | 112 | if [ -n "$filter_patterns" ]; then | |
| 96 | 113 | local tree_pattern; tree_pattern=$(echo "$filter_patterns" | tr ',' '|') | |
| 97 | 114 | tree_args+=(-P "$tree_pattern") | |
| 98 | - | echo "(筛选模式: ${filter_patterns})" | |
| 115 | + | echo "(Filter active: ${filter_patterns})" | |
| 99 | 116 | fi | |
| 100 | - | echo "(动态调整深度至 L${best_depth} 以适应预览数限制: ${max_items_preview})" | |
| 117 | + | echo "(Dynamic depth set to L${best_depth} to fit item limit: ${max_items_preview})" | |
| 101 | 118 | tree "${tree_args[@]}" "$target_dir" | |
| 102 | 119 | echo | |
| 103 | 120 | ||
| 104 | - | # --- 步骤 4: 遍历并打印文件内容 (应用筛选和深度限制) --- | |
| 121 | + | # --- Step 4: Iterate and print file contents (applying filters and depth limits) --- | |
| 105 | 122 | local header_suffix="" | |
| 106 | - | if [ -n "$filter_patterns" ]; then header_suffix=" (筛选: ${filter_patterns})"; fi | |
| 107 | - | echo "📄 文件内容详情 (深度 L${best_depth} 以内, 单文件最多 ${max_file_lines} 行${header_suffix}):" | |
| 123 | + | if [ -n "$filter_patterns" ]; then header_suffix=" (filter: ${filter_patterns})"; fi | |
| 124 | + | echo "📄 File Content Details (within depth L${best_depth}, max ${max_file_lines} lines per file${header_suffix}):" | |
| 108 | 125 | ( | |
| 109 | 126 | cd "$target_dir" || exit 1 | |
| 110 | 127 | echo "----------------------------------------------------------" | |
| @@ -113,18 +130,19 @@ glance() { | |||
| 113 | 130 | [ -n "$filter_patterns" ] && IFS=',' read -ra filter_array <<< "$filter_patterns" | |
| 114 | 131 | ||
| 115 | 132 | if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then | |
| 116 | - | # Git 模式 | |
| 133 | + | # Git mode | |
| 117 | 134 | local git_files | |
| 118 | 135 | if [ ${#filter_array[@]} -gt 0 ]; then | |
| 119 | 136 | git_files=$(git ls-files -- "${filter_array[@]}") | |
| 120 | 137 | else | |
| 121 | 138 | git_files=$(git ls-files) | |
| 122 | 139 | fi | |
| 140 | + | # Use awk to filter paths by slash count to simulate depth limit | |
| 123 | 141 | echo "$git_files" | awk -F'/' -v depth="$best_depth" 'NF <= depth' | while read -r file; do | |
| 124 | 142 | _glance_process_file "$file" | |
| 125 | 143 | done | |
| 126 | 144 | else | |
| 127 | - | # 普通模式 | |
| 145 | + | # Normal mode | |
| 128 | 146 | local find_args=(-maxdepth "$best_depth" -type f) | |
| 129 | 147 | if [ ${#filter_array[@]} -gt 0 ]; then | |
| 130 | 148 | find_args+=("(") | |
| @@ -134,7 +152,9 @@ glance() { | |||
| 134 | 152 | done | |
| 135 | 153 | find_args+=(")") | |
| 136 | 154 | fi | |
| 155 | + | # Use find's -maxdepth parameter and name patterns | |
| 137 | 156 | find . "${find_args[@]}" | while read -r file; do | |
| 157 | + | # Remove leading './' from file path for cleaner output | |
| 138 | 158 | _glance_process_file "${file#./}" | |
| 139 | 159 | done | |
| 140 | 160 | fi | |
anduin ревизій цього gist 2 months ago. До ревизії
Без змін
anduin ревизій цього gist 2 months ago. До ревизії
1 file changed, 142 insertions
glance.bash(файл створено)
| @@ -0,0 +1,142 @@ | |||
| 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 | + | } | |