function's calling context in zsh: equivalent of bash `caller`
I don't think there's a builtin command equivalent, but some combination of these four variables from the zsh/Parameter module can be used:
funcfiletrace
This array contains the absolute line numbers and corresponding file names for the point where the current function, sourced file, or (if
EVAL_LINENO
is set)eval
command was called. The array is of the same length asfuncsourcetrace
andfunctrace
, but differs fromfuncsourcetrace
in that the line and file are the point of call, not the point of definition, and differs fromfunctrace
in that all values are absolute line numbers in files, rather than relative to the start of a function, if any.funcsourcetrace
This array contains the file names and line numbers of the points where the functions, sourced files, and (if
EVAL_LINENO
is set)eval
commands currently being executed were defined. The line number is the line where the ‘function name
’ or ‘name ()
’ started. In the case of an autoloaded function the line number is reported as zero. The format of each element isfilename:lineno
.For functions autoloaded from a file in native zsh format, where only the body of the function occurs in the file, or for files that have been executed by the
source
or ‘.
’ builtins, the trace information is shown asfilename:0
, since the entire file is the definition. The source file name is resolved to an absolute path when the function is loaded or the path to it otherwise resolved.Most users will be interested in the information in the
funcfiletrace
array instead.funcstack
This array contains the names of the functions, sourced files, and (if
EVAL_LINENO
is set)eval
commands. currently being executed. The first element is the name of the function using the parameter.The standard shell array
zsh_eval_context
can be used to determine the type of shell construct being executed at each depth: note, however, that is in the opposite order, with the most recent item last, and it is more detailed, for example including an entry for toplevel, the main shell code being executed either interactively or from a script, which is not present in$funcstack
.functrace
This array contains the names and line numbers of the callers corresponding to the functions currently being executed. The format of each element is
name:lineno
. Callers are also shown for sourced files; the caller is the point where thesource
or ‘.
’ command was executed.
Comparing:
foo.bash
:
#! /bin/bash
yelp() {
caller 0
}
foo () {
yelp
}
foo
foo.zsh
:
#! /bin/zsh
yelp() {
print -l -- $funcfiletrace - $funcsourcetrace - $funcstack - $functrace
}
foo () {
yelp
}
foo
The results:
$ bash foo.bash
7 foo foo.bash
$ zsh foo.zsh
foo.zsh:7
foo.zsh:10
-
foo.zsh:2
foo.zsh:6
-
yelp
foo
-
foo:1
foo.zsh:10
So, the corresponding values are in ${funcfiletrace[1]}
and ${funcstack[-1]}
. Modifying yelp
to:
yelp() {
print -- $funcfiletrace[1] $funcstack[-1]
}
The output is:
foo.zsh:7 foo
which is quite close to bash's
7 foo foo.bash
Based on muru's answer, I implemented the following function which works in both {ba,z}sh
:
$ cat yelp
#!/bin/zsh
# Say the file, line number and optional message for debugging
# Inspired by bash's `caller` builtin
# Thanks to https://unix.stackexchange.com/a/453153/143394
function yelp () {
# shellcheck disable=SC2154 # undeclared zsh variables in bash
if [[ $BASH_VERSION ]]; then
local file=${BASH_SOURCE[1]} func=${FUNCNAME[1]} line=${BASH_LINENO[0]}
else # zsh
emulate -L zsh # because we may be sourced by zsh `emulate bash -c`
# $funcfiletrace has format: file:line
local file=${funcfiletrace[1]%:*} line=${funcfiletrace[1]##*:}
local func=${funcstack[2]}
[[ $func =~ / ]] && func=source # $func may be filename. Use bash behaviour
fi
echo "${file##*/}:$func:$line $*" > /dev/tty
}
foo () { yelp; }
yelp
foo
The output is:
$ ./yelp
yelp::20
yelp:foo:19