zsh中函数的调用上下文:等同于bash`caller`


8

在bash中,我可以写:

caller 0

并接收呼叫者上下文的:

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

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

yelp () { caller 0; }

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

我可以实现caller 0bash如下:

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

我怎样才能获得相同的输出caller 0zsh

Answers:


14

我认为没有等效的内置命令,但是可以使用zsh / Parameter模块中这四个变量的某种组合:

funcfiletrace

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

funcsourcetrace

此数组包含的文件名,并且其中所述的功能,源文件,和点的行号(如果EVAL_LINENO是组)eval 被定义命令当前正在执行。行号是“ function name”或“ name ()”开始的行。对于自动加载的功能,行号报告为零。每个元素的格式为filename:lineno

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

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

funcstack

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

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

functrace

该数组包含与当前正在执行的功能相对应的调用方的名称和行号。每个元素的格式为name:lineno。还会显示源文件的调用方。调用者是执行“ source.”命令的地方。

比较:

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

3

根据muru的回答,我实现了以下两个功能都可以使用的功能{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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.