Last active
September 5, 2025 05:33
-
-
Save bulatovv/0bf0c2d03a890a22c4857cc56b5f61c4 to your computer and use it in GitHub Desktop.
Revisions
-
bulatovv revised this gist
Sep 5, 2025 . No changes.There are no files selected for viewing
-
bulatovv revised this gist
Sep 5, 2025 . No changes.There are no files selected for viewing
-
bulatovv revised this gist
Sep 5, 2025 . 1 changed file with 85 additions and 49 deletions.There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -1,22 +1,23 @@ #!/bin/sh # treecat - A script to display directory structure or file contents for multiple paths. # (Generated with Gemini Pro 2.5) # --- Default Configuration --- MAX_SIZE=102400 # 100 KB SHOW_HIDDEN=false USE_GITIGNORE=true # TARGET_PATH is now handled by arguments # --- Helper Functions --- # Print usage information and exit. usage() { cat <<EOF Usage: $(basename "$0") [options] [path1] [path2] ... Displays the directory structure and file contents for one or more paths. If no path is given, it defaults to the current directory. Options: -a, --show-hidden Show hidden files and directories (those starting with '.'). @@ -27,10 +28,9 @@ Options: EOF } # Check if a file is binary. is_binary() { # A file is considered binary if its first 512 bytes contain any non-printable characters. if head -c 512 "$1" | LC_ALL=C grep -q '[^[:print:][:space:]]'; then return 0 # Has non-text characters -> binary else @@ -41,8 +41,7 @@ is_binary() { # Traverse upwards from a given directory to find the root of a .git repository. find_git_root() { path="$1" path="$(cd "$path" 2>/dev/null && pwd)" # Resolve to absolute path while [ -n "$path" ] && [ "$path" != "/" ]; do if [ -d "$path/.git" ]; then printf "%s\n" "$path" @@ -82,48 +81,61 @@ while [ "$#" -gt 0 ]; do exit 1 ;; *) # Not an option, so it must be a path. Stop option processing. break ;; esac done # If no paths are left after parsing options, default to the current directory. if [ "$#" -eq 0 ]; then set -- "." fi # --- Processing Functions --- # Processes and displays the content of a single file. # $1: The file path to process. process_file() { local file_path="$1" printf "📄 %s\n" "$(basename "$file_path")" size=$(wc -c < "$file_path") if [ "$size" -gt "$MAX_SIZE" ]; then printf " [file too large: %s bytes]\n" "$size" return fi if is_binary "$file_path"; then printf " [binary file]\n" return fi # Use sed to add a prefix to each line for consistent indentation. sed 's/^/│ /' "$file_path" } # The recursive function to process a directory. # $1: The path to process (relative to the chdir'd location). # $2: The prefix for printing tree lines. # $3: The git repository root, if found. process_directory() { local path="$1" local base_prefix="$2" local git_root="$3" items=$(find "$path" -mindepth 1 -maxdepth 1 | sort) if [ "$SHOW_HIDDEN" = false ]; then items=$(printf "%s\n" "$items" | grep -v '/\.') fi if [ -z "$items" ]; then return fi item_count=$(printf "%s\n" "$items" | wc -l) current_item=0 @@ -132,14 +144,12 @@ process_path() { basename=$(basename "$item") # Filter based on gitignore if applicable if [ -n "$git_root" ]; then if git -C "$git_root" check-ignore -q "$item"; then continue fi fi if [ "$current_item" -eq "$item_count" ]; then connector="└──" content_prefix="${base_prefix} " @@ -152,18 +162,18 @@ process_path() { if [ -d "$item" ]; then printf "%s%s 📁 %s/\n" "$base_prefix" "$connector" "$basename" process_directory "$item" "$next_base_prefix" "$git_root" elif [ -f "$item" ]; then printf "%s%s 📄 %s\n" "$base_prefix" "$connector" "$basename" size=$(wc -c < "$item") if [ "$size" -gt "$MAX_SIZE" ]; then printf "%s[file too large: %s bytes]\n" "$content_prefix" "$size" continue fi if is_binary "$item"; then printf "%s[binary file]\n" "$content_prefix" continue fi @@ -174,14 +184,40 @@ process_path() { done } # --- Main Logic --- is_first_arg=true for TARGET_PATH in "$@"; do # Add a separator between outputs for multiple arguments if [ "$is_first_arg" = true ]; then is_first_arg=false else printf "\n---\n\n" fi if [ -d "$TARGET_PATH" ]; then # Run in a subshell to prevent `cd` from affecting subsequent arguments. ( ABS_TARGET_DIR="$(cd "$TARGET_PATH" 2>/dev/null && pwd)" if [ -z "$ABS_TARGET_DIR" ]; then printf "Error: Could not access target directory: %s\n" "$TARGET_PATH" >&2 exit 1 fi GIT_ROOT="" if [ "$USE_GITIGNORE" = true ] && command -v git >/dev/null 2>&1; then GIT_ROOT=$(find_git_root "$ABS_TARGET_DIR") fi printf "📁 %s/\n" "$(basename "$TARGET_PATH")" cd "$TARGET_PATH" || exit 1 process_directory "." "" "$GIT_ROOT" ) elif [ -f "$TARGET_PATH" ]; then # --- It's a file, run the single-file logic --- process_file "$TARGET_PATH" else # --- It's neither a file nor a directory --- printf "Error: Path not found or is not a regular file/directory: %s\n" "$TARGET_PATH" >&2 fi done -
bulatovv created this gist
Sep 4, 2025 .There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,187 @@ #!/bin/sh # treecat - A script to display directory structure and file contents. # (Generated using Gemini Pro 2.5) # --- Default Configuration --- MAX_SIZE=102400 # 100 KB SHOW_HIDDEN=false USE_GITIGNORE=true TARGET_DIR="." # --- Helper Functions --- # Print usage information and exit. usage() { cat <<EOF Usage: $(basename "$0") [options] [directory] Displays the directory structure and file contents in a tree format. Options: -a, --show-hidden Show hidden files and directories (those starting with '.'). --no-gitignore Ignore .gitignore files and show all files. --max-size BYTES Set the maximum file size in bytes for displaying content. (Default: ${MAX_SIZE}) -h, --help Show this help message. EOF } # Check if a file is binary. A file is considered binary if its first 512 # bytes contain any non-printable characters (excluding whitespace). # Returns 0 (true) if binary, 1 (false) otherwise. is_binary() { if head -c 512 "$1" | LC_ALL=C grep -q '[^[:print:][:space:]]'; then return 0 # Has non-text characters -> binary else return 1 # No non-text characters found -> text fi } # Traverse upwards from a given directory to find the root of a .git repository. find_git_root() { path="$1" # Resolve to an absolute path to handle `cd ..` safely path="$(cd "$path" 2>/dev/null && pwd)" while [ -n "$path" ] && [ "$path" != "/" ]; do if [ -d "$path/.git" ]; then printf "%s\n" "$path" return fi path=$(dirname "$path") done } # --- Argument Parsing --- while [ "$#" -gt 0 ]; do case "$1" in -a|--show-hidden) SHOW_HIDDEN=true shift ;; --no-gitignore) USE_GITIGNORE=false shift ;; --max-size) if [ -z "$2" ] || ! [ "$2" -eq "$2" ] 2>/dev/null; then printf "Error: --max-size requires a numeric argument.\n" >&2 exit 1 fi MAX_SIZE="$2" shift 2 ;; -h|--help) usage exit 0 ;; -*) printf "Unknown option: %s\n" "$1" >&2 usage exit 1 ;; *) TARGET_DIR="$1" shift ;; esac done # --- Main Logic --- if ! [ -d "$TARGET_DIR" ]; then printf "Error: Directory not found: %s\n" "$TARGET_DIR" >&2 exit 1 fi # Determine the absolute path of the target for gitignore checks BEFORE changing directory. ABS_TARGET_DIR="$(cd "$TARGET_DIR" 2>/dev/null && pwd)" if [ -z "$ABS_TARGET_DIR" ]; then printf "Error: Could not access target directory: %s\n" "$TARGET_DIR" >&2 exit 1 fi # Find the git repository root if enabled. GIT_ROOT="" if [ "$USE_GITIGNORE" = true ] && command -v git >/dev/null 2>&1; then GIT_ROOT=$(find_git_root "$ABS_TARGET_DIR") fi # The main recursive function to process a directory. # $1: The path to process (relative to the chdir'd location). # $2: The prefix for printing tree lines. process_path() { local path="$1" local base_prefix="$2" items=$(find "$path" -mindepth 1 -maxdepth 1 | sort) if [ "$SHOW_HIDDEN" = false ]; then items=$(printf "%s\n" "$items" | grep -v '/\.') fi if [ -z "$items" ]; then return fi item_count=$(printf "%s\n" "$items" | wc -l) current_item=0 printf "%s\n" "$items" | while IFS= read -r item; do current_item=$((current_item + 1)) basename=$(basename "$item") # Filter based on gitignore if applicable if [ -n "$GIT_ROOT" ]; then # Use -C to run git from the repo root, checking the path from find. # Since we chdir'd, "$item" is already the correct relative path. if git -C "$GIT_ROOT" check-ignore -q "$item"; then continue fi fi if [ "$current_item" -eq "$item_count" ]; then connector="└──" content_prefix="${base_prefix} " next_base_prefix="${base_prefix} " else connector="├──" content_prefix="${base_prefix}│ " next_base_prefix="${base_prefix}│ " fi if [ -d "$item" ]; then printf "%s%s 📁 %s/\n" "$base_prefix" "$connector" "$basename" process_path "$item" "$next_base_prefix" elif [ -f "$item" ]; then printf "%s%s 📄 %s\n" "$base_prefix" "$connector" "$basename" size=$(wc -c < "$item") if [ "$size" -gt "$MAX_SIZE" ]; then printf "%s [file too large: %s bytes]\n" "$base_prefix" "$size" continue fi if is_binary "$item"; then printf "%s [binary file]\n" "$base_prefix" continue fi while IFS= read -r line || [ -n "$line" ]; do printf "%s%s\n" "$content_prefix" "$line" done < "$item" fi done } # --- Initial Call --- # Print the initial root directory name based on the original argument. printf "📁 %s/\n" "$(basename "$TARGET_DIR")" # Change to the target directory to simplify path handling for `find` and `git`. cd "$TARGET_DIR" || exit 1 # Start the recursive processing from the current directory ("."). process_path "." ""