Skip to content

Instantly share code, notes, and snippets.

@tecnologer
Last active January 21, 2026 23:28
Show Gist options
  • Select an option

  • Save tecnologer/b918978fbec82674a205092434ee5de8 to your computer and use it in GitHub Desktop.

Select an option

Save tecnologer/b918978fbec82674a205092434ee5de8 to your computer and use it in GitHub Desktop.
Recursive directory tree with permissions and ownership (stat-tree)

Stat Tree

A Bash script that displays a recursive directory tree with file permissions (octal), owner:group information, and color-coded output (blue for directories, green for files).

Similar to the tree command but includes detailed stat information in a clean format.

Usage:

./stat-tree.sh /path/to/directory

Output example:

755 user:group subdir1/
├── 755 user:group abc/
│   └── 755 user:group xyz/
├── 644 user:group file.txt
└── 755 user:group subdir2/

4 directories, 1 file

Features:

  • Displays octal permissions (e.g., 755, 644)
  • Shows owner and group for each item
  • Color-coded output (directories in blue, files in green)
  • Proper tree structure with UTF-8 box drawing characters
  • Sorted output for consistent results
  • Summary count of directories and files
#!/bin/bash
dir="${1:-.}"
# Color codes
DIR_COLOR="\033[1;34m" # Bold blue for directories
FILE_COLOR="\033[0;92m" # Green color for files
RESET="\033[0m" # Reset color
# Counters
total_dirs=0
total_files=0
# Function to generate tree structure
print_tree() {
local prefix="$1"
local dir_path="$2"
local items=()
# Read directory contents into array
while IFS= read -r -d '' item; do
items+=("$item")
done < <(find "$dir_path" -maxdepth 1 -mindepth 1 -print0 | sort -z)
local count=${#items[@]}
local i=0
for item in "${items[@]}"; do
i=$((i + 1))
local basename=$(basename "$item")
local perms=$(stat -c "%a" "$item")
local owner=$(stat -c "%U" "$item")
local group=$(stat -c "%G" "$item")
# Determine if this is the last item
if [ $i -eq $count ]; then
local connector="└── "
local new_prefix="${prefix} "
else
local connector="├── "
local new_prefix="${prefix}│ "
fi
# Print with permissions, ownership, and color
if [ -d "$item" ]; then
echo -e "${prefix}${connector}${perms} ${owner}:${group} ${DIR_COLOR}${basename}/${RESET}"
((total_dirs++))
print_tree "$new_prefix" "$item"
else
echo -e "${prefix}${connector}${perms} ${owner}:${group} ${FILE_COLOR}${basename}${RESET}"
((total_files++))
fi
done
}
# Check if target is a file or directory
if [ -f "$dir" ]; then
# It's a file, just print it
perms=$(stat -c "%a" "$dir")
owner=$(stat -c "%U" "$dir")
group=$(stat -c "%G" "$dir")
echo -e "${perms} ${owner}:${group} ${FILE_COLOR}$(basename "$dir")${RESET}"
total_files=1
elif [ -d "$dir" ]; then
# It's a directory, print with trailing slash and process contents
perms=$(stat -c "%a" "$dir")
owner=$(stat -c "%U" "$dir")
group=$(stat -c "%G" "$dir")
echo -e "${perms} ${owner}:${group} ${DIR_COLOR}$(basename "$dir")/${RESET}"
total_dirs=1
# Print tree
print_tree "" "$dir"
else
echo "Error: $dir does not exist or is not accessible"
exit 1
fi
# Print summary
echo ""
if [ $total_dirs -eq 1 ]; then
dir_text="directory"
else
dir_text="directories"
fi
if [ $total_files -eq 1 ]; then
file_text="file"
else
file_text="files"
fi
echo "$total_dirs $dir_text, $total_files $file_text"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment