zsh 中函数的调用上下文:相当于 bash `caller`

zsh 中函数的调用上下文:相当于 bash `caller`

在 bash 中,我可以这样写:

caller 0

并收到呼叫者上下文的:

  • 电话号码
  • 功能
  • 脚本名称

这对于调试非常有用。鉴于:

yelp () { caller 0; }

然后我可以写信yelp查看正在到达哪些代码行。

我可以实施caller 0bash

echo "${BASH_LINENO[0]} ${FUNCNAME[1]} ${BASH_SOURCE[1]"

caller 0我怎样才能得到与中相同的输出zsh

答案1

我不认为有内置的命令等价的,但是这四个变量的某种组合zsh/参数模块可以使用:

funcfiletrace

EVAL_LINENO该数组包含调用当前函数、源文件或(如果已设置)命令的位置的绝对行号和相应的文件名 eval。该数组与funcsourcetrace和具有相同的长度functrace,但不同之处 funcsourcetrace在于行和文件是调用点,而不是定义点,并且不同之处在于functrace所有值都是文件中的绝对行号,而不是相对于函数的开始(如果有)。

funcsourcetrace

此数组包含定义当前正在执行的函数、源文件和(如果EVAL_LINENO设置了)命令的点的文件名和行号。行号是“ ”或“ ”开始的行。对于自动加载的函数,行号报告为零。每个元素的格式为。evalfunction namename ()filename:lineno

对于从本机 zsh 格式的文件自动加载的函数(其中仅函数主体出现在文件中),或者对于已由source或 ' .' 内置函数执行的文件,跟踪信息显示为filename:0,因为整个文件是定义。加载函数时,源文件名将解析为绝对路径,否则将解析其路径。

大多数用户会对数组中的信息感兴趣 funcfiletrace

funcstack

该数组包含函数的名称、源文件和(如果 EVAL_LINENO已设置)eval命令。目前正在执行。第一个元素是使用该参数的函数的名称。

标准 shell 数组zsh_eval_context可用于确定在每个深度执行的 shell 构造的类型:但是请注意,顺序相反,最后一项是最新的项,并且更详细,例如包括一个条目toplevel,主要的 shell 代码以交互方式或从脚本执行,这在$funcstack.

functrace

该数组包含与当前正在执行的函数相对应的调用者的名称和行号。每个元素的格式为name:lineno.还会显示源文件的调用者;调用者是执行sourceor ' ' 命令的点。.

比较:

foo.bash:

#! /bin/bash
yelp() {
    caller 0
}

foo () {
    yelp
}

foo

foo.zsh:

#! /bin/zsh
yelp() {
    print -l -- $funcfiletrace - $funcsourcetrace - $funcstack - $functrace
}

foo () {
    yelp
}

foo

结果:

$ 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

因此,相应的值在${funcfiletrace[1]}和中${funcstack[-1]}。修改yelp为:

yelp() {
    print -- $funcfiletrace[1] $funcstack[-1]
}

输出是:

foo.zsh:7 foo

这与 bash 非常接近

7 foo foo.bash

答案2

基于穆鲁的回答,我实现了以下功能,该功能适用​​于两者{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

输出是:

$ ./yelp
yelp::20 
yelp:foo:19

相关内容