Skip to content

Instantly share code, notes, and snippets.

@niskala5570
Created July 27, 2025 09:01
Show Gist options
  • Save niskala5570/9288f883bb42c0221c03064760b58b30 to your computer and use it in GitHub Desktop.
Save niskala5570/9288f883bb42c0221c03064760b58b30 to your computer and use it in GitHub Desktop.
#!/bin/bash
# Fancy Tree with emoji, color, depth, size, style and filtering 🌲
# Color codes
C_RESET="\033[0m"
C_FOLDER="\033[1;34m"
C_FILE="\033[0;37m"
C_SIZE="\033[0;90m"
C_EXEC="\033[1;32m"
C_SCRIPT="\033[0;35m"
# Tree styles
set_tree_style() {
case "$1" in
ascii) TEE="|--"; ELB="\\--"; PIPE="| "; SPAC=" " ;;
dotted) TEE="β”Šβ”€β”€"; ELB="β”Šβ”€β”€"; PIPE="β”Š "; SPAC=" " ;;
round) TEE="╭──"; ELB="╰──"; PIPE="β”‚ "; SPAC=" " ;;
double) TEE="╠══"; ELB="β•šβ•β•"; PIPE="β•‘ "; SPAC=" " ;;
*) TEE="β”œβ”€β”€"; ELB="└──"; PIPE="β”‚ "; SPAC=" " ;; # default
esac
}
# Human-readable file size
get_size() {
if [ -f "$1" ]; then
stat --format="%s" "$1" | awk '
function human(x) {
s="B KB MB GB TB PB"
split(s, units)
while (x>=1024 && i<5) {x/=1024; ++i}
return sprintf("%.1f%s", x, units[i+1])
}
{print human($1)}'
else
echo ""
fi
}
# Determine emoji + color
get_icon_and_color() {
local f="$1"
local name="$(basename "$f")"
local icon="πŸ“„"
local color="$C_FILE"
if [ -d "$f" ]; then icon="πŸ“"; color="$C_FOLDER"
# Images
elif [[ "$name" =~ \.(jpg|jpeg|png|gif|svg)$ ]]; then icon="πŸ–ΌοΈ"
# Video
elif [[ "$name" =~ \.(mp4|mkv|mov|avi)$ ]]; then icon="🎞️"
# Audio
elif [[ "$name" =~ \.(mp3|wav|flac)$ ]]; then icon="🎡"
# Archives
elif [[ "$name" =~ \.(zip|tar|gz|rar|7z)$ ]]; then icon="πŸ“¦"
# Scripts
elif [[ "$name" =~ \.(sh|bash)$ ]]; then icon="🐚"; color="$C_SCRIPT"
# Code
elif [[ "$name" =~ \.(py|pl|rb|js|ts|cpp|c|hpp|java|rs)$ ]]; then icon="πŸ’»"; color="$C_SCRIPT"
# Documents
elif [[ "$name" =~ \.(pdf|docx?|txt|md)$ ]]; then icon="πŸ“„"
# Subtitles
elif [[ "$name" =~ \.(srt|vtt)$ ]]; then icon="πŸ“"; color="$C_FILE"
elif [[ "$name" =~ \.(ass|ssa)$ ]]; then icon="🎨"; color="$C_SCRIPT"
elif [[ "$name" =~ \.(sub|idx)$ ]]; then icon="🧩"; color="$C_FILE"
# Executables
elif [[ -x "$f" ]]; then icon="βš™οΈ"; color="$C_EXEC"
fi
echo "$icon|$color"
}
# Main recursive printer
print_tree() {
local dir="$1"
local prefix="$2"
local depth="$3"
[ "$MAX_DEPTH" -ge 0 ] && [ "$depth" -gt "$MAX_DEPTH" ] && return
local entries=("$dir"/*)
local count=${#entries[@]}
for i in "${!entries[@]}"; do
local path="${entries[$i]}"
[ ! -e "$path" ] && continue
local name="$(basename "$path")"
# Apply filter
if [ -n "$FILTER_EXT" ] && [ -f "$path" ]; then
[[ ! "$name" =~ \.$FILTER_EXT$ ]] && continue
fi
local connector="$TEE"
local new_prefix="${prefix}${PIPE}"
if [ $((i+1)) -eq "$count" ]; then
connector="$ELB"
new_prefix="${prefix}${SPAC}"
fi
IFS="|" read -r icon color <<< "$(get_icon_and_color "$path")"
local size=""
[ "$SHOW_SIZE" = true ] && size="$(get_size "$path")"
[ -n "$size" ] && size=" $C_SIZE[$size]$C_RESET"
local line="${prefix}${connector} ${icon} ${color}${name}${C_RESET}${size}"
echo -e "$line"
[ -n "$OUTPUT_FILE" ] && echo -e "$(echo "$line" | sed 's/\x1B\[[0-9;]*[a-zA-Z]//g')" >> "$OUTPUT_FILE"
if [ -d "$path" ]; then
print_tree "$path" "$new_prefix" $((depth+1))
fi
done
}
# Default values
STYLE="default"
TARGET="."
SHOW_SIZE=false
FILTER_EXT=""
MAX_DEPTH=-1
OUTPUT_FILE=""
# Parse args
while [[ "$#" -gt 0 ]]; do
case "$1" in
--style=*) STYLE="${1#*=}" ;;
--depth=*) MAX_DEPTH="${1#*=}" ;;
--ext=*) FILTER_EXT="${1#*=}" ;;
--size) SHOW_SIZE=true ;;
--out=*) OUTPUT_FILE="${1#*=}"; rm -f "$OUTPUT_FILE" ;;
-h|--help)
echo "Usage: ./fancytree.sh [options] [directory]"
echo "Options:"
echo " --style=STYLE Tree style (default, ascii, dotted, round, double)"
echo " --size Show file sizes"
echo " --depth=N Limit depth to N levels"
echo " --ext=EXT Filter files by extension (e.g. mp4)"
echo " --out=FILE Output plain text to FILE"
echo " -h, --help Show this help"
exit 0 ;;
*) TARGET="$1" ;;
esac
shift
done
set_tree_style "$STYLE"
echo -e "πŸ“‚ $TARGET"
[ -n "$OUTPUT_FILE" ] && echo "πŸ“‚ $TARGET" > "$OUTPUT_FILE"
print_tree "$TARGET" "" 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment