Last active
April 20, 2026 15:34
-
-
Save bcantoni/7f5a6c55cb1a2b4ae40a0c0acd05e145 to your computer and use it in GitHub Desktop.
Analyze subdirectories and show key development details at a glance.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| usage() { | |
| cat <<EOF | |
| Usage: glance <directory> | |
| Analyze subdirectories and show key development details at a glance. | |
| Columns: Name, Git, URL, Changes, Last Commit, Last Changed, Size | |
| EOF | |
| exit 0 | |
| } | |
| [[ $# -eq 0 || "$1" == "-h" ]] && usage | |
| TARGET="${1%/}" | |
| if [[ ! -d "$TARGET" ]]; then | |
| echo "Error: '$TARGET' is not a directory" >&2 | |
| exit 1 | |
| fi | |
| IS_MAC=false | |
| [[ "$(uname)" == "Darwin" ]] && IS_MAC=true | |
| transform_git_url() { | |
| local url="$1" | |
| url="${url%.git}" | |
| if [[ "$url" =~ ^git@ ]]; then | |
| url="${url#git@}" | |
| url="https://${url/://}" | |
| fi | |
| echo "$url" | |
| } | |
| format_epoch() { | |
| local epoch="$1" | |
| if $IS_MAC; then | |
| date -r "$epoch" "+%Y-%m-%d" 2>/dev/null || echo "-" | |
| else | |
| date -d "@$epoch" "+%Y-%m-%d" 2>/dev/null || echo "-" | |
| fi | |
| } | |
| last_modified() { | |
| local dir="$1" | |
| local epoch | |
| if $IS_MAC; then | |
| epoch=$(find "$dir" -type f -not -path '*/.*' -print0 2>/dev/null | xargs -0 stat -f "%m" 2>/dev/null | sort -rn | head -1) | |
| else | |
| epoch=$(find "$dir" -type f -not -path '*/.*' -printf "%T@\n" 2>/dev/null | sort -rn | head -1 | cut -d. -f1) | |
| fi | |
| if [[ -n "$epoch" ]]; then | |
| format_epoch "$epoch" | |
| else | |
| echo "-" | |
| fi | |
| } | |
| dir_size() { | |
| du -sh "$1" 2>/dev/null | cut -f1 || echo "-" | |
| } | |
| HEADERS=("NAME" "GIT" "URL" "CHANGES" "LAST COMMIT" "LAST CHANGED" "SIZE") | |
| rows=() | |
| while IFS= read -r -d '' subdir; do | |
| name=$(basename "$subdir") | |
| git_val="no" | |
| url_val="-" | |
| changes_val="-" | |
| commit_val="-" | |
| if [[ -d "$subdir/.git" ]]; then | |
| git_val="yes" | |
| raw_url=$(git -C "$subdir" remote get-url origin 2>/dev/null || true) | |
| if [[ -n "$raw_url" ]]; then | |
| url_val=$(transform_git_url "$raw_url") | |
| fi | |
| if [[ -z "$(git -C "$subdir" status --porcelain 2>/dev/null)" ]]; then | |
| changes_val="no" | |
| else | |
| changes_val="yes" | |
| fi | |
| commit_epoch=$(git -C "$subdir" log -1 --format="%ct" 2>/dev/null || true) | |
| if [[ -n "$commit_epoch" ]]; then | |
| commit_val=$(format_epoch "$commit_epoch") | |
| fi | |
| fi | |
| changed_val=$(last_modified "$subdir") | |
| size_val=$(dir_size "$subdir") | |
| rows+=("$name|$git_val|$url_val|$changes_val|$commit_val|$changed_val|$size_val") | |
| done < <(find "$TARGET" -maxdepth 1 -mindepth 1 -type d -not -name '.*' -print0 | sort -z) | |
| if [[ ${#rows[@]} -eq 0 ]]; then | |
| echo "No subdirectories found in '$TARGET'" | |
| exit 0 | |
| fi | |
| ncols=${#HEADERS[@]} | |
| widths=() | |
| for h in "${HEADERS[@]}"; do | |
| widths+=(${#h}) | |
| done | |
| for row in "${rows[@]}"; do | |
| IFS='|' read -ra fields <<< "$row" | |
| for i in "${!fields[@]}"; do | |
| len=${#fields[$i]} | |
| if [[ $len -gt ${widths[$i]} ]]; then | |
| widths[$i]=$len | |
| fi | |
| done | |
| done | |
| header_line="" | |
| sep_line="" | |
| for i in "${!HEADERS[@]}"; do | |
| w=${widths[$i]} | |
| pad=$(printf '%*s' "$w" '') | |
| header_line+=$(printf "%-${w}s" "${HEADERS[$i]}") | |
| sep_line+="${pad// /-}" | |
| if [[ $i -lt $((ncols - 1)) ]]; then | |
| header_line+=" " | |
| sep_line+=" " | |
| fi | |
| done | |
| echo "$header_line" | |
| echo "$sep_line" | |
| for row in "${rows[@]}"; do | |
| IFS='|' read -ra fields <<< "$row" | |
| line="" | |
| for i in "${!fields[@]}"; do | |
| line+=$(printf "%-${widths[$i]}s" "${fields[$i]}") | |
| if [[ $i -lt $((ncols - 1)) ]]; then | |
| line+=" " | |
| fi | |
| done | |
| echo "$line" | |
| done |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Spec by me, code from Claude Code