這是什麼 zsh 語法?名稱()真
我假設以下塊代表一個函式,但它可能不是:
mounted()true if ... fi ... && mounted
我想了解那個構造是什麼。我在zsh的函式標題下沒有找到類似的語法
更完整的實際程式碼片段是:
#! /bin/zsh -p # leaving out a section... tmpdir=$(mktemp -d) || exit mounted()true if mount "$type[@]" -o "${(j[,])opts}" -- "$dev" "$tmpdir" then mount --bind -- "$tmpdir/$subdir" "$dest" || mounted()false umount -- "$tmpdir" fi && rmdir -- "$tmpdir" && mounted
一旦我理解了它,我將把整個 zsh 腳本轉換成我更熟悉的語言。我可能會將其轉換為 bash 作為中間步驟。
那是定義函式的 Bourne shell 語法(從 80 年代開始),它並不特定於 zsh。
在 Bourne shell 中,函式是通過粘貼
functionName()
在命令前面來定義的¹。所以
name() true
定義了一個name
用true
簡單命令呼叫的函式作為它的主體。$ name()true $ type name name is a shell function
幾乎所有的 Bourne-like shell(ksh、ash、dash、bosh、pdksh、mksh、zsh ……)都是這種情況。一個值得注意的例外是
bash
²(GNU shell),它最初只允許命令組({ ...; }
) 作為函式的主體,後來將其更改為POSIX語言要求的任何復合命令。sh
因此,在 中
bash
,您需要name()((1))
orname()[[ . ]]
((( ... ))
和[[ ... ]]
— 從 ksh 借來的構造 — 被視為那裡的複合命令),或者name() { true; }
函式的主體是一個命令組,裡面只有一個簡單的命令。請注意,這
ksh
是首先引入函式的 shell,儘管語法不同:function name { body; }
.zsh
支持 Korn 和 Bourne 語法,並有自己的擴展。在
zsh
seeinfo zsh function
中,您應該會看到關於函式定義語法的 Kornfunction
關鍵字的部分。一個擴展zsh
是您可以使用相同的主體一次定義多個函式,並使用任何字元串作為函式名稱³:vrai 1 + $'\1' () true faux 0 - $'\0' '' () false
定義幾個不同的呼叫
true
或false
不帶參數的函式。如果你省略函式名,它會變成一個匿名函式,它可以接受參數並被當場呼叫:
() { echo There are $# non-hidden txt files; } *.txt(N)
(儘管出於顯而易見的原因,對於能夠接受參數的匿名函式,它的主體不能是一個簡單的命令)。
在
zsh
中,函式也可以通過$functions
特殊的關聯數組獲得,其中鍵是函式名,值是主體中的程式碼。functions[name]=true
定義name
函式的另一種方法也是如此。現在,使用函式來儲存布爾值並不是你經常看到的,但如果你停下來想一想,它確實很有意義。
例如,在 C 語言中,
if
/while
構造或&&
/||
邏輯運算符作用於數字。ifif (condition) something
是一個非零數。或者哪些是類 C 語言將其擴展到被定義或成為非空字元串。something``condition``awk``perl``condition
但是 shell 在所有命令行解釋器之前。在 shell 中,一切都是命令。
if
/while
和&&
/||
在大多數 shell 中作用於命令。if condition; then something; fi
如果命令成功something
則執行。condition
false
並且true
是總是分別失敗/成功的布爾常量命令(內置在大多數 shell 中),因此它們是表示布爾值的明顯命令。函式是儲存命令(或一般的 shell 程式碼)的最合適的資料結構。
alias
es(許多人認為它是 csh 的破壞遺產,一個 shell(就像最初的 Bourne shell)沒有功能)在這裡不起作用,因為別名在讀取包含它們的程式碼時被擴展,而不是在執行時。例如在:alias name=false myfunction() if name; then something fi alias name=true myfunction
函式體實際上將包含
if false; then...
,因為name
別名是在讀取定義函式的程式碼時擴展的,而不是在函式執行時。可以將程式碼儲存在變數而不是函式中:
name=true name=false if eval " $name"; then ... fi
我們在哪裡測試命令是否成功,該
eval
命令被告知解釋$name
4中的程式碼。請注意,在這種情況下,空/未定義(nounset
選項除外)會$name
產生true。或者我們可以這樣做(這就是我通常在
sh
/bash
腳本中所做的):name=true name=false if "$name"; then ... fi
我們在其中執行名稱儲存在
$name
沒有參數的命令的位置。或者:
name=(true) name=(false) if "${name[@]}"; then ... fi
我們將簡單命令的參數儲存在
$name
數組變數中。習慣於使用類 C 語言的人可能希望將布爾值儲存為整數變數並執行諸如
[
/之類的命令,test
或者expr
在從文本表示轉換時測試整數的值:false=0 true=1 name=$false name=$true if expr "$name" > /dev/null; then ... fi if [ "$name" -ne 0 ]; then ... fi
在類似 Korn 的 shell(包括
bash
和zsh
)中,您可以使用((...))
計算類似 C 的算術表達式並在產生非零的數字(甚至是 NaN)時返回成功的構造。false=0 true=1 name=$false name=$true # or even: name=false name=true if (( name )); then ... fi
您還可以執行一個比較字元串的命令(例如
[
/test
/expr
再次),或者像其他類似 Korn 的[[ string = pattern ]]
構造一樣進行模式匹配:name=true name=false if [ "$name" = true ]; then ... fi
(這聽起來對我來說就像
if (strcmp(name, "true") == 0)...
在 C 中做 a 一樣陌生)。或者即使您喜歡,也可以對已定義的變數進行類似
awk
/perl
的測試。unset -v name # unset = false name= # set = true if [ -n "${name+true}" ]; then ... fi
¹ Bourne shell 中有一個錯誤(不是在它的複製/衍生物5中),但是如果您使用帶有重定向的簡單命令作為函式的主體,則該錯誤無法正常工作,這可能是 POSIX 只需要復合命令的原因被支持為函式體。
²
yash
(根據 POSIX 規範編寫),posh
(基於pdksh
但編寫以幫助驗證是否符合標準,因此刪除了對標準的大多數擴展,包括該標準)是另外兩個例外³ 這與外部命令和命令參數可以是任何字元串的事實是一致的,因為文件可以包含任何字元串(儘管文件名不能為空並且文件名/參數不能包含 NUL 字節)。在 Bourne shell 中,函式和變數名稱共享相同的命名空間(您不能定義具有相同名稱的函式和變數),並且函式名稱與變數名稱具有相同的限制。
4前面有一個空格以確保正確性,因為某些
eval
實現不支持--
標記選項的結尾5 zsh,在函式體中使用重定向時,本身曾經有自己的問題,其中大部分已修復。但即使是現在,正如文件中明確指出的那樣,in
f() { cmd; } < $1
,(在主體是具有重定向的命令組的特殊情況下)$1
不是指$1
函式範圍內的,而是$1
指呼叫者的該實例使其不符合 POSIX 標準。這不適用於簡單命令或其他類型的複合命令(zsh
內部包含在 中{...}
)。