我刚刚决定尝试 zsh (通过 oh-my-zsh),现在正在尝试precmd
模拟一个两行提示,该提示不仅仅在最后一行中有正确的提示。
所以我克隆了默认主题,并受到启发这个帖子(我也用它来学习很多东西),我做了这样的事情(稍后我会添加颜色):
function precmd {
local cwd="${(%):-[%~]}"
local who_where="${(%):-%n@%m}"
local git_info=${(%)$(git_prompt_info)}
local right_prompt=" $git_info [$who_where]"
local left_prompt="${(r:(($COLUMNS - ${#${right_prompt}})):: :)cwd}"
echo "$left_prompt$right_prompt"
}
它有效。但我忍不住想知道:zsh 是否在每次调用 precmd 时定义所有这些变量?
我一直在谷歌搜索与 zsh 相关的闭包、作用域和命名空间,希望将本地变量作为数据附加到 precmd,因此不需要每次都重新定义变量,但我什么也没找到。有什么方法可以做我正在尝试的事情,还是我应该放弃它?
作为旁注,并且仅当相关时,“加载函数”是什么意思?
答案1
Zsh 没有诸如闭包、包或命名空间之类的东西。 Zsh 缺少一些实现真正的闭包所需的东西:
功能不是一流的。您不能将函数作为参数传递给其他函数,并且函数不能返回其他函数。 (您可以通过姓名要调用的函数的名称,但这与传递函数本身不同)。
不能有嵌套函数。 zsh 中的所有函数都是全局的。您必须为函数名称添加前缀以避免冲突。特别注意,函数将隐藏同名的外部程序。如果您有一个名为 的函数
ls
,则将调用该函数而不是程序ls
。这可能很有用,除非您不小心这样做了。变量的作用域是动态的,而不是像大多数现代语言那样是静态的。即使您可以有嵌套函数,内部函数也不会按照您通常期望的方式关闭外部函数的局部变量。你不能像人们用 Javascript 那样使用它们来制作模块。
兹什做有匿名函数,但如果没有任何其他东西,它们就没有多大用处。
所以基本上,你能做的最好的事情就是为所有函数和全局变量添加前缀。
我还要指出你应该precmd
像这样定义你的:
% autoload -Uz add-zsh-hook
% add-zsh-hook precmd my_precmd_function
add-zsh-hook
让您挂钩您的函数,precmd
而不会覆盖任何其他可能也想挂钩的函数precmd
。
加载函数意味着什么是一个单独的问题。 Zsh 具有自动加载功能,仅在实际调用函数时才从磁盘加载函数。当您这样做时,就可以调用autoload -Uz foobar
名为的函数。foobar
当您实际调用 时foobar
,会从磁盘加载定义。
答案2
不,闭包对于 zsh 来说太复杂了。 Zsh 旨在解释与直接交互相距不远的小脚本。它不具备对于大型编程非常有用的花哨语言功能,但对于 shell 通常用于执行的小型任务则不太有用。
请注意,如果存在某种形式的闭包,允许一次一劳永逸地预先计算变量的值然后存储,那么当某些变化导致信息变得无效时,这些值将不会被更新。
$git_info
由于对签入 git 或 git 存储库的文件进行修改,派生变量可以随时更改。所以无论如何它们每次都需要重新计算。
cwd
您可以将和的值缓存who_where
在全局变量中,因为它们在正常操作下不会改变。cwd
当前目录更改时会更改,因此需要从chpwd
.然而,这些变量的计算速度非常快,因此没有必要费心。这里昂贵的计算正在运行git_prompt_info
,并且可能随时改变。
当您在每个命令之间显示信息时,最好将其作为提示(PS1
或psvar
数组)的一部分。 Zsh 知道它必须在各种情况下重新显示提示,但它对您打印的内容一无所知precmd
。
答案3
是的,每次调用该函数时都会(重新)定义这些变量。
如果您只想初始化它们一次,您可以简单地将它们移至函数的顶层。
答案4
为了实现闭包,语言需要能够将函数作为元素或对象进行操作,这在 zsh 中是不可能的,除非通过字符串和内置函数eval
(就像在其他 shell 中一样)。但这是非常有限的,因为您需要自己处理所有低级的东西(例如引用)。然而,当参数没有特殊字符时(因此不需要处理引用),使用这种想法很容易做一些简单的事情,并且在 zsh 中,匿名函数可以提供一点帮助。例如,要定义计算 的函数a*x+b*y
,其中a
和b
是在函数定义时提供的常量,x
并且y
是函数的参数:
mk_ax_plus_by() { echo "() { echo \$((($1)*(\$1)+($2)*(\$2))) }" }
fct_2x_plus_3y=$(mk_ax_plus_by 2 3)
fct_5x_plus_7y=$(mk_ax_plus_by 5 7)
因此,有一个fct_2x_plus_3y
计算函数2*x+3*y
和一个fct_5x_plus_7y
计算函数5*x+7*y
(请注意,我选择函数名称只是为了便于阅读,这些名称可以是任何名称,您甚至不需要将内容存储在变量中)。另请注意,这些实际上是字符串(不是 shell 术语中的函数),但它们的行为类似于eval
内置函数。使用示例:
% eval $fct_2x_plus_3y 4 9
35
% eval $fct_5x_plus_7y 4 9
83
2*4+3*9
给出 35 并给出5*4+7*9
83。
请注意,与函数式语言不同,这里需要区分来自环境的参数(未加引号)和定义函数的参数(加引号),即仅在函数调用时评估。但是可以将实现更改为更像函数式语言,并且用法将是相同的:
mk_ax_plus_by()
{
local x='$1' y='$2'
echo "() { echo \$((($1)*($x)+($2)*($y))) }"
}