Enhanced bash / zsh which command
Originally published
Last modified
I usually just use which when I want to know about where a command comes from. Sometimes, I expect the command to be a symlink, or I want to know the filesize or last modified date, so I then routinely ls -l
the output of which to check.
Why not make that automatic? Let's automatically run ls -l on all files in which output. This is possible with the Function Command Extension Trick.
Unfortunately, zsh and bash have some of those occasional implementation differences requiring specific versions of the which function:
- zsh has a builtin
which
; bash does not - both zsh and bash have builtin
type
- zsh
type -f
displays function definitions; bashtype -f
suppresses function definitions - busybox / dash does not have
builtin
It is cumbersome and error-prone to add code to handle all these differences in the same function, so I have 2 different versions:
Zsh version of augmented which
unalias which &>/dev/null # Prevent system-wide which alias from breaking the which function
# When which output is a file, ls -l the file (zsh-specific version)
which() {
{ [[ -t 0 ]] && [[ -t 1 ]]; } || { builtin which "$@"; return; }
local which_out which_exit
which_out="$(builtin which -x4 "$@")"
which_exit="$?"
[[ -z "$which_out" ]] && return "$which_exit"
printf '%s\n' "$which_out" | while IFS=$'\n' read -r line; do
if [[ "$line" = "/"* ]] && [[ -x "$line" ]]; then
# eval to use ls alias with color flag. Aliases only work if set before function definition
eval "ls -la $(printf %q "$line")"
else
printf '%s\n' "$line"
fi
done
return "$which_exit"
}
Bash version of augmented which
unalias which &>/dev/null # Prevent system-wide which alias from breaking the which function
# When which output is a file, ls -l the file (bash-specific version)
which() {
{ [[ -t 0 ]] && [[ -t 1 ]]; } || { command which "$@"; return; }
local which_out which_exit
which_out="$(builtin type "$@")"
which_exit="$?"
[[ -z "$which_out" ]] && return "$which_exit"
printf '%s\n' "$which_out" | while IFS=$'\n' read -r line; do
if [[ "$line" = *" is /"* ]] && [[ -x "${line/#* is /}" ]]; then
printf '%s' "${line%% is *} is "
# eval to use ls alias with color flag. Aliases only work if set before function definition
eval "ls -la $(printf %q "${line/#* is /}")"
else
printf '%s\n' "$line"
fi
done
return "$which_exit"
}