Skip to content

Instantly share code, notes, and snippets.

@mevanlc
Created September 6, 2025 23:03
Show Gist options
  • Save mevanlc/62bf0c38bc1cb5edcf048c73d2127f4d to your computer and use it in GitHub Desktop.
Save mevanlc/62bf0c38bc1cb5edcf048c73d2127f4d to your computer and use it in GitHub Desktop.
what_is.sh that command?
#!/usr/bin/env bash
# what_is.sh - Inspect how Bash would resolve a command name.
# Modes:
# (A) Source to define the function: . what_is.sh
# Then use: what_is <cmd>
# (B) Source-and-run once: . what_is.sh <cmd>
# (C) Execute (cannot see live aliases/functions in this shell):
# ./what_is.sh <cmd>
what_is() {
# Usage: what_is <cmd>
if [ $# -ne 1 ]; then
printf -- "Usage: what_is <command>\n" >&2
return 2
fi
local cmd=$1
printf -- "Command: %s\n" "$cmd"
printf -- "Shell: bash %s\n\n" "${BASH_VERSION:-unknown}"
# Must not `exit` here; this runs in the caller when sourced.
if ! type -a -- "$cmd" >/dev/null 2>&1; then
printf -- "Not found: %s (return 127)\n" "$cmd"
return 127
fi
printf -- "== type -a output (all resolutions) ==\n"
type -a -- "$cmd"
printf -- "\n"
# 1) Alias
if alias "$cmd" >/dev/null 2>&1; then
printf -- "---- Alias ----\n"
alias "$cmd"
printf -- "\n"
fi
# 2) Function (+ source location if extdebug is available)
if declare -F "$cmd" >/dev/null 2>&1; then
printf -- "---- Function ----\n"
# Remember extdebug state, enable temporarily if needed.
local had_extdebug=0
if shopt -q extdebug; then
had_extdebug=1
else
shopt -s extdebug 2>/dev/null || true
fi
# With extdebug, `declare -F name` prints: name line file
local meta fn_name fn_line fn_file
meta=$(declare -F "$cmd" 2>/dev/null || true)
set -- $meta
fn_name=${1:-}
fn_line=${2:-}
fn_file=${3:-}
if [ -n "$fn_line" ] && [ -n "$fn_file" ]; then
printf -- "Defined at: %s:%s\n" "$fn_file" "$fn_line"
else
printf -- "Defined at: (location not available; try 'shopt -s extdebug')\n"
fi
# Restore extdebug if we enabled it.
if [ "$had_extdebug" -eq 0 ]; then
shopt -u extdebug 2>/dev/null || true
fi
printf -- "Source code:\n"
declare -f "$cmd"
printf -- "\n"
fi
# 3) Keyword (reserved word)
if compgen -A keyword -- "$cmd" | grep -qx -- "$cmd"; then
printf -- "---- Keyword ----\n"
printf -- "'%s' is a shell reserved word (keyword).\n" "$cmd"
help "$cmd" 2>/dev/null | sed -n '1,12p' || true
printf -- "\n"
fi
# 4) Builtin (even if shadowed elsewhere)
if compgen -b | grep -qx -- "$cmd"; then
printf -- "---- Builtin ----\n"
printf -- "'%s' is a shell builtin.\n" "$cmd"
help "$cmd" 2>/dev/null | sed -n '1,20p' || true
printf -- "\n"
fi
# 5) Files on PATH (scripts or binaries). Show all unique paths.
printf -- "---- PATH File(s) ----\n"
local paths=() seen_list=""
# `type -aP` = all, path search only (files)
while IFS= read -r p; do
[ -z "$p" ] && continue
# dedupe without assoc arrays (portable to bash 3.x)
if printf -- "%s\n" "$seen_list" | grep -Fxq -- "$p"; then
:
else
paths+=("$p")
seen_list="${seen_list}${seen_list:+
}$p"
fi
done < <(type -aP -- "$cmd" 2>/dev/null || true)
if [ "${#paths[@]}" -eq 0 ]; then
printf -- "(no disk file found in PATH for '%s')\n\n" "$cmd"
else
local hashed_path have_file resolved ft
hashed_path=$(hash -t -- "$cmd" 2>/dev/null || true)
if command -v file >/dev/null 2>&1; then
have_file=1
else
have_file=0
fi
_resolve_path() {
# best-effort path resolution without assuming GNU utils
local t=$1 out
if command -v realpath >/dev/null 2>&1; then
realpath -- "$t" 2>/dev/null || true
elif readlink -f -- "$t" >/dev/null 2>&1; then
readlink -f -- "$t" 2>/dev/null || true
else
readlink -- "$t" 2>/dev/null || true
fi
}
local p
for p in "${paths[@]}"; do
printf -- "Path: %s\n" "$p"
resolved=$(_resolve_path "$p")
if [ -n "$resolved" ] && [ "$resolved" != "$p" ]; then
printf -- "Resolved: %s\n" "$resolved"
fi
if [ "$have_file" -eq 1 ]; then
ft=$(file -b -- "$p" 2>/dev/null || true)
[ -n "$ft" ] && printf -- "File type: %s\n" "$ft"
fi
if head -n 1 -- "$p" 2>/dev/null | grep -q '^#!'; then
printf -- "Shebang: %s\n" "$(head -n 1 -- "$p" 2>/dev/null)"
fi
if [ -L "$p" ] 2>/dev/null; then
ls -l -- "$p" 2>/dev/null | sed 's/^/ls -l: /'
fi
if [ -n "$hashed_path" ] && [ "$hashed_path" = "$p" ]; then
printf -- "(This path is currently hashed in this shell)\n"
fi
printf -- "\n"
done
fi
# Primary resolution
local primary
primary=$(type -t -- "$cmd" 2>/dev/null || true)
if [ -n "$primary" ]; then
printf -- "== Primary resolution used by this shell ==\n"
case "$primary" in
alias) printf -- "%s is an alias.\n" "$cmd" ;;
function) printf -- "%s is a function.\n" "$cmd" ;;
keyword) printf -- "%s is a keyword (reserved word).\n" "$cmd" ;;
builtin) printf -- "%s is a builtin.\n" "$cmd" ;;
file) printf -- "%s is a disk file (script or binary).\n" "$cmd" ;;
*) printf -- "%s has type: %s\n" "$cmd" "$primary" ;;
esac
fi
}
# Detect whether we are sourced.
# If sourced: define function; if args were provided, run once.
# If executed: run once, but warn about alias/function visibility.
if [ "${BASH_SOURCE[0]}" != "$0" ]; then
# Sourced
if [ $# -gt 0 ]; then
what_is "$@"
else
printf -- "Defined function: what_is\n"
printf -- "Usage: what_is <command>\n"
fi
else
# Executed
if [ $# -eq 0 ]; then
printf -- "Usage:\n . %s [<command>] # preferred; sees live aliases/functions\n %s <command> # executed; cannot see live aliases/functions\n" "$0" "$0" >&2
exit 2
fi
# Run and forward status
what_is "$@"
exit $?
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment