Scripting

zsh login init 和手動採購配置文件腳本之間的不同行為是什麼?

  • December 30, 2020

我想在 ZSH 登錄時向 precmd_functions 添加一個函式並避免重複。由於 /etc/zprofile 獲取 /etc/profile,然後獲取 /etc/profile.d/ 下的所有 *.sh 腳本,我的解決方案是將初始化腳本添加到 /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 specificationif (( $precmd_functions...在行中報告。

那麼為什麼只有 login init 報告這個錯誤,而手動 source 腳本不呢?

下的文件/etc/profile.d預計將以 sh 語法編寫,因此推測該/etc/zprofile源中的程式碼/etc/profile.d/*安排以 sh 或 ash 仿真模式執行此操作,可能類似於emulate ksh -c '. /etc/profile'.

sh 和 zsh 語法之間的區別之一是它在 zsh 中$foo[bar]被解析為數組變數取消引用,但在 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 …函式之外:它不是源腳本的本地。)

引用自:https://unix.stackexchange.com/questions/626604