包含函数的环境变量是bash hack。Zsh没有类似的东西。您可以使用几行代码来执行类似的操作。环境变量包含字符串;在发现Shellshock之前,较早的bash版本将函数的代码存储在一个变量中,该变量的名称为函数的名称,其值() {
后跟函数的代码}
。您可以使用以下代码以这种编码导入变量,并尝试使用类似bash的设置运行它们。请注意,zsh无法模拟所有bash功能,您所能做的就是变得更近一点(例如,$foo
将值分割并扩展通配符,并使数组从0开始)。
bash_function_preamble='
emulate -LR ksh
'
for name in ${(k)parameters}; do
[[ "-$parameters[name]-" = *-export-* ]] || continue
[[ ${(P)name} = '() {'*'}' ]] || continue
((! $+builtins[$name])) || continue
functions[$name]=$bash_function_preamble${${${(P)name}#"() {"}%"}"}
done
(正如Shellshock的原始发现者StéphaneChazelas指出的那样,如果函数定义的格式有误,此答案的早期版本此时可以执行任意代码。该版本没有,但是当然只要您执行任何命令,它可能是从环境导入的函数。)
在环境中使用无效的变量名(例如BASH_FUNC_myfunc%%
)对bash的Shellshock后版本进行编码。由于zsh没有提供从环境中提取此类变量名称的接口,因此这使它们更难以可靠地解析。
我不建议这样做。依靠脚本中的导出函数是一个坏主意:它在脚本中创建了不可见的依赖关系。如果您曾经在没有功能的环境中运行脚本(在更改了外壳初始化文件后,在另一台计算机上,在cron作业中,…),则该脚本将不再起作用。而是将所有函数存储在一个或多个单独的文件中(如~/lib/shell/foo.sh
),然后通过导入其使用的函数(. ~/lib/shell/foo.sh
)来启动脚本。这样,如果您进行修改foo.sh
,则可以轻松地搜索依赖它的脚本。如果您复制脚本,则可以轻松找出它需要哪些辅助文件。
Zsh(及其之前的ksh)通过提供一种在使用脚本的函数中自动加载函数的方式,使此操作更加方便。约束是每个文件只能放置一个函数。将函数声明为自动加载,并将函数定义放入名称为函数名称的文件中。将此文件放置在中列出的目录中$fpath
(您可以通过FPATH
环境变量进行配置)。在脚本中,使用声明自动加载的函数autoload -U foo
。
此外,zsh可以编译脚本,以节省解析时间。调用zcompile
以编译脚本。这将创建带有.zwc
扩展名的文件。如果存在此文件,autoload
则将加载编译的文件而不是源代码。您可以使用该zrecompile
函数来(重新)编译目录中的所有函数定义。