我剛剛決定嘗試 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))) }"
}