zsh: 获取调用函数的脚本文件名

3

在一个函数内部,如何获取调用该函数的脚本文件名?

通过提示展开,%x提供定义函数的文件名。 %N提供函数名称。我该如何获取调用该函数的文件名?

这对于报告每个启动脚本的运行时非常有用,例如:

~/.zshenv:

function start_timer() {
  start_time=$(date +%s.%N)
}
function stop_timer() {
  stop_time=$(date +%s.%N)
  elapsed_time=$((stop_time-start_time))
  echo "$SCRIPTNAME took $elapsed_time seconds"
}
start_timer
# initialize env...
end_timer # "~/.zshenv took 0 seconds"

~/.zshrc:

start_timer
# initialize interactive shell...
end_timer # "~/.zshrc took 2 seconds"

~/.zlogin:

start_timer
# initialize login shell...
end_timer # "~/.zlogin took 2 seconds"

这里有一个非常相似的问题,但是也没有真正的解决方案。 - Lucas
3个回答

5

至少在zsh 5.7.1中,ZSH_ARGZERO变量会报告当前脚本的名称,即使在不同的函数内调用它也是如此:

$ cat foo
function a_function_in_foo {
    echo "ZSH_ARGZERO=$ZSH_ARGZERO"
}
$ cat bar
source foo
a_function_in_foo
$ zsh bar
ZSH_ARGZERO=bar

官方文档中有详细说明,网址为http://zsh.sourceforge.net/Doc/Release/Parameters.html

ZSH_ARGZERO

如果是通过执行脚本来启动 zsh,则该变量表示该脚本的名称;否则,它表示用于调用当前 shell 的名称。当 POSIX_ARGZERO 选项被设置时,此值与 $0 相同,但始终可用。


在Debian上的zsh 5.8中:当脚本从终端调用时,函数中的$ZSH_ARGZERO会回显zsh,而当使用带有zsh-debug扩展的vscode运行时,则会回显~/.vscode/extensions/rogalmic.zsh-debug-0.1.3/zshdb_dir/zshdb - Timo

1
你可以将名称作为参数传递。

~/.zshenv:

function start_timer() {
  echo "File: `basename $@`"             # <----
  start_time=$(date +%s.%N)
}

~/.zshrc:

start_timer $0                           # <----
# initialize interactive shell...
end_timer # "~/.zshrc took 2 seconds"
< p > < em > basename 只在您只想显示文件名时删除最后一个斜杠之前的所有内容。

< p > 我的测试:

 File: aliases.zsh
 took 0.0018649101257324219 seconds

如果您自己调用脚本,您也可以使用time作为前缀命令来打印它们的执行时间。
$ time touch x
touch x  0.00s user 0.00s system 7% cpu 0.019 total

当zsh在初始化时被调用时,每个启动文件中的$0都是简单的zsh。如果我直接将脚本名称传递给函数,比如 end_time ${(%):-%x},那么它可以工作,但你可以看到为什么我不想在每次调用时使用它 :) - mgiuffrida

1

$0 对于来源脚本无效。尝试以下两个选项之一,这两个选项在我的 .zshrc* 中添加后都有效:

选项1

使用 ${(%):-%N}${(%):-%x} 获取源脚本的路径(这两个扩展对我都有效)。

sourcedFile=${(%):-%1N};
echo "${sourcedFile} took ${sec} second(s) to load.";

输出:

.zshrc加载花费了2秒钟。

选项2)

使用lsof +p $$获取在被zsh源时所需脚本的名称。然后,使用类似于grep的工具提取所需行,并将其分配给变量。然后,您可以使用zsh扩展来提取行的最后一个“组件”:

sourcedFile=$(lsof +p $$ | grep zshrc);
echo "${sourcedFile:t0} took ${sec} second(s) to load.";

输出:

.zshrc 文件加载耗时 2 秒。

显然,选项 2 由于使用了 lsof,所以需要更多的循环。


以上的扩展示例基于zsh文档。请参阅man zshexpn>HISTORY EXPANSION > Modifiersman zshmisc>SIMPLE PROMPT ESCAPES>Shell state以获取更详细的解释。
关于选项1,来自man zshmisc
   %N     The name of the script, sourced file, or shell function that zsh is currently executing, whichever was
          started most recently.  If there is none, this is equivalent to the parameter $0.  An integer may
          follow the `%' to specify a number of trailing path components to show; zero means the full path.  A
          negative integer specifies leading components.
对于上述的选项1,请参考this post作为我的解决方案来源。
* 注意:我目前使用的是zsh 5.8.1(x86_64-apple-darwin22.0),即macOS 13.2.1,在此环境下我已成功测试了这两个选项。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接