zsh 登录 init 和手动获取配置文件脚本之间的行为有何不同?

zsh 登录 init 和手动获取配置文件脚本之间的行为有何不同?

我想在 ZSH 登录时向 precmd_functions 添加一个函数并避免重复。由于 /etc/zprofile 来源 /etc/profile,然后来源 /etc/profile.d/ 下的所有 *.sh 脚本,我的解决方案是将 init 脚本添加到 /etc/profile.d。为了与 bash 保持兼容,自动源脚本new_script.sh如下:

# zsh user
if [ -n "$ZSH_VERSION" ]; then
    source /etc/profile.d/new_script.zsh
# bash user
elif [ -n "$BASH_VERSION" ]; then
    source /etc/profile.d/new_script.bash
fi

到这里一切都是绿色的,但随后 new_script.zsh 做出了奇怪的行为。它的内容如下:

...    
    if (( $precmd_functions[(I)audit_hook] )); then
        hook_exist=true
    else
        hook_exist=false
    fi

当我使用 zsh 登录后手动获取它时,它运行没有任何问题。但是当在登录初始化过程中自动获取时,它会bad output format specification在行中报告if (( $precmd_functions...

那么为什么只有登录 init 才会报告此错误,而手动获取脚本却不会报告此错误?

答案1

预计下的文件/etc/profile.d将以 sh 语法编写,因此推测/etc/zprofile源中的代码/etc/profile.d/*安排在 sh 或 ash 仿真模式下执行此操作,可能类似于emulate ksh -c '. /etc/profile'

sh 和 zsh 语法之间的区别之一是,$foo[bar]在 zsh 中被解析为数组变量取消引用,但在 sh(以及兼容的 shell,如 bash 和 zsh)中,它是一个字符串变量,后跟一个单字符 glob(因此在 sh 中touch somefilename1 somefilename2 somefilename3; var=somefilename; echo $var[12]打印) somefilename1 somefilename2/bash/ksh,但e在 zsh 中)。当 zsh 处于 sh 或 ksh 模拟模式时,zsh_arrays选项已启用,因此它不会解析$precmd_functions[(I)audit_hook]为数组访问。precmd_functions此时为空(如果不是,您很可能会得到一些不同的错误),因此算术表达式最终为[(I)audit_hook]。 zsh 中的括号部分算术表达式是一个输出格式规范,指示输出以不同的基础进行格式化(以及一些其他可能性,这就是为什么它不仅仅是一个输出基础规范)。当 zsh 看到左括号时,它会准备解析输出格式规范,但会失败。

如果您编写特定于 zsh 的代码/etc/profile.d并希望使用 zsh 语法,请明确告诉 zsh 使用 zsh 语法:

if [ -n "$ZSH_VERSION" ]; then
    emulate zsh -c 'source /etc/profile.d/new_script.zsh'
fi

或者将所有代码放入new_script.zsh一个函数中并放入emulate -L zsh在函数的顶部。 (不要放在emulate -L …函数之外:它不是源脚本的本地函数。)

相关内容