Zsh

這是什麼 zsh 語法?名稱()真

  • November 17, 2021

我假設以下塊代表一個函式,但它可能不是:

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定義了一個nametrue簡單命令呼叫的函式作為它的主體。

$ 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))or name()[[ . ]]((( ... ))[[ ... ]]— 從 ksh 借來的構造 — 被視為那裡的複合命令),或者name() { true; }函式的主體是一個命令組,裡面只有一個簡單的命令。

請注意,這ksh是首先引入函式的 shell,儘管語法不同:function name { body; }. zsh支持 Korn 和 Bourne 語法,並有自己的擴展。

zshseeinfo zsh function中,您應該會看到關於函式定義語法的 Kornfunction關鍵字的部分。一個擴展zsh是您可以使用相同的主體一次定義多個函式,並使用任何字元串作為函式名稱³:

vrai 1 + $'\1' () true
faux 0 - $'\0' '' () false

定義幾個不同的呼叫truefalse不帶參數的函式。

如果你省略函式名,它會變成一個匿名函式,它可以接受參數並被當場呼叫:

() { 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 程式碼)的最合適的資料結構。

aliases(許多人認為它是 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命令被告知解釋$name4中的程式碼。請注意,在這種情況下,空/未定義(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(包括bashzsh)中,您可以使用((...))計算類似 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內部包含在 中{...})。

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