Skip to content

Instantly share code, notes, and snippets.

@kbauer
Last active August 11, 2017 09:55
Show Gist options
  • Save kbauer/4a846eadd78716f9033c84b30e638551 to your computer and use it in GitHub Desktop.
Save kbauer/4a846eadd78716f9033c84b30e638551 to your computer and use it in GitHub Desktop.
A pure bash implementation of the `tree` utility, for when the `tree` binary isn't available. Avoids subshells or subprocesses to improve performance.
#!/usr/bin/env bash
# A fallback, when GNU tree isn't installed.
# Example:
# >>> mkdir -p {A1,A2}/{B1,B2/C1}
# >>> touch A2/B3
# >>> my-tree
# .
# |-- A1
# | |-- B1
# | \-- B2
# | \-- C1
# \-- A2
# |-- B1
# |-- B2
# | \-- C1
# \-- B3
#
# 8 directories, 1 files
# >>>
#
# Avoids subshells and subprocesses to give better speed.
set -e -E -u
shopt -s nullglob # * -> empty in empty directory
shopt -s dotglob # * -> include .hidden files but not ../parent.
NUMFILES=0
NUMDIRS=-1 # top doesn't count
tree_do () {
local INDENT="$1" PREFIX="$2" ROOT="$3" POS="$4"
case "$POS" in
nonlast) local ADDINDENT='| ';;
last) local ADDINDENT=' ';;
top) local ADDINDENT='';;
*)
echo "Invalid value POS=$POS"
exit 1
;;
esac
local NEWINDENT="$INDENT$ADDINDENT"
if ! [[ -d "$ROOT" ]]; then
printf "%s\n" "$INDENT$PREFIX$ROOT"
: $((++NUMFILES))
else
: $((++NUMDIRS))
printf "%s\n" "$INDENT$PREFIX$ROOT"
local OLDPWD="$PWD"
cd "$ROOT" || return 0
local FILE="" NEXTFILE
for NEXTFILE in *; do
# Print for not-last file.
if [[ -n "$FILE" ]]; then
tree_do "$NEWINDENT" "|-- " "$FILE" nonlast
fi
FILE="$NEXTFILE"
done
# Print for last file - if any.
if [[ -n "$FILE" ]]; then
tree_do "$NEWINDENT" "\-- " "$FILE" last
fi
cd "$OLDPWD"
fi
}
tree_do "" "" "${1:-.}" top
echo ''
echo "$NUMDIRS directories, $NUMFILES files"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment