不少時候,咱們寫的代碼並非只運行一次就再也不用了,那就須要保存到文件裏。咱們一般稱包含解釋性編程語言代碼的可執行文件爲腳本文件,簡稱腳本。而在腳本內部,也會有一些能夠複用的代碼,咱們能夠把這樣的代碼寫成函數,供其餘部分調用。Zsh 中函數和腳本基本上同樣的,能夠認爲腳本就是以文件名爲函數名的函數。腳本和函數的編寫方法基本相同,因此在一塊兒講。git
先從函數開始,由於涉及更少的細節。github
# 一個很簡單的函數
fun() {
echo good
}
# 也能夠在前邊加一個 function 關鍵字
function fun() {
echo good
}複製代碼
這樣就能夠定義一個函數了。小括號必定是空的,即便函數有參數,也無需在裏邊寫參數列表。編程
直接輸入函數名便可調用函數。數組
fun() {
echo good
}
% fun
good複製代碼
用 unfunction 能夠刪除函數。bash
fun() {
echo good
}
% unfunction fun
% fun
zsh: command not found: fun複製代碼
函數能夠有參數,但 zsh 中無需顯式註明有幾個參數,直接讀取便可。微信
fun() {
echo $1 $2 $3
echo $#
}
% fun aa
aa
1
% fun aa bb cc
aa bb cc
3
% fun aa bb cc dd
aa bb cc
4複製代碼
$n 是第 n 個參數,$# 是參數個數。若是讀取的時候沒有對應參數傳進來,那和讀取一個未定義的變量效果是同樣的。函數的參數只能是字符串類型,若是把整數、浮點數傳進函數裏,也會被轉成字符串。能夠把數組傳給函數,而後數組中的元素會依次成爲各個參數。編程語言
fun() {
echo $1 $2 $3
echo $#
}
% array=(11 22 33)
% fun $array
11 22 33
3複製代碼
這樣用的好處是能夠更方便地處理帶空格的參數。ide
# 遍歷全部參數,$* 是包含全部參數的數組
fun() {
for i ($*) {
echo $i
}
}
% fun a b c
a
b
c複製代碼
能夠用 $+n 快速判斷第 n 個參數是否存在。函數
fun() {
(($+1)) && {
echo $1
}
}複製代碼
關於 $* 和 $@。在 bash 中, $* 和 $@ 的區別是一個比較麻煩的事情,但在 zsh 中,一般沒有必要使用 $@,因此不用踩這個坑。Bash 中須要使用 $@ 的緣由是若是使用 $* 而且參數中有空格的話,就分不清哪些空格是參數裏的,哪些空格是參數之間的間隔符(bash 裏的 $* 是一個字符串)。而若是使用 "$*" 的話,全部的參數都合併成一個字符串了。而 "$@" 能夠保留參數中的空格,因此一般使用 "$@"。可是有些時候須要把全部參數拼接成一個字符串,那麼又要使用 "$*",因此很混亂。ui
而 zsh 中的 $* 會包括參數中的空格(zsh 裏的 $* 是一個數組),因此效果和 bash 的 "$@" 是差很少的。另外在 zsh 中用 "$*" 和在 bash 中的 "$*" 效果同樣,因此只用 $* 和 "$*" 就足夠了。
函數能夠嵌套定義。
fun() {
fun2() {
echo $2
}
fun2 $1 $2
}
% fun aa bb
bb複製代碼
fun2 函數是在 fun 執行過纔會被定義的,但最外邊也能直接訪問 fun2 函數。若是想要最外邊訪問不了,能夠在 fun 結束前調用 unfunction fun2 刪除 fun2 函數。
函數須要返回一個表明函數是否正確執行的返回值,若是是 0,表明正確執行,若是不是 0,表明有錯誤。
#!/bin/zsh
fun() {
(($+1)) && {
return
}
return 1
}
% fun 111 && echo good
good
% fun || echo bad
bad
% fun
# 也能夠用 $? 獲取函數返回值
% echo $?複製代碼
遇到 return 後,函數當即結束。return 即 return 0。
注意返回值不是用來返回數據的,若是函數須要將字符串、整數、浮點數等返回給調用者,直接用 echo 或者 print 等命令輸出便可,而後調用者用 $(fun) 獲取。若是須要返回數組或者哈希表,只能經過變量(全局變量或者函數所在層次的局部變量)傳遞。
fun() {
echo 123.456
}
% echo $($(fun) *2))
246.91200000000001複製代碼
經過全局變量返回。
array=()
fun() {
array=(aa bb)
}
% fun
% echo $array
aa bb複製代碼
在函數中能夠直接讀寫函數外邊的變量,而且在函數中定義的新變量在函數退出後依然存在。
str1=abcd
fun() {
echo $str1
str2=1234
}
% fun
abcd
% echo $str2
1234複製代碼
這一般是不符合預期的。爲了不函數內的變量「滲透」到函數外,可使用局部變量,使用 local 定義變量。
str1=abcd
fun() {
echo $str1
local str2=1234
}
% fun
abcd
% echo $str2複製代碼
函數中的變量,除非確實須要留給外部使用,否則最好所有使用局部變量,避免引起 bug。
能夠認爲腳本也是一個函數,但它是單獨寫到一個文件裏的。
test.zsh 內容。
#!/bin/zsh
echo good複製代碼
這是一個很是簡單的腳本文件。第一行是固定的,供系統找到 zsh 解釋器,#! 後加 zsh 的絕對路徑便可。若是須要使用環境變量訪問,能夠用 #!/bin/env zsh (或者 !/usr/bin/env zsh,若是 env 在 /usr/bin/ 裏邊)。
從第二行開始,就和函數中的內容同樣了。上邊函數體裏的內容(去掉首尾行的 fun() { 和 },均可以寫在這裏邊。
執行的話,在 test.zsh 所在目錄,運行 zsh test.zsh 加參數便可(就像調用了一個名爲 zsh test.zsh 的函數。也能夠 chmod u+x test.zsh 給它添加可執行權限後,直接運行 ./test.zsh 加參數。
腳本的參數和返回值的處理方法,和函數的徹底同樣,這裏就不舉例了。
但函數和腳本中執行的時候是有區別的,函數是在當前的 zsh 進程裏執行(也能夠調用的時候加小括號在子進程執行),而腳本是在新的子進程裏執行,執行完子進程即退出了,因此腳本中的變量值外界是訪問不到的,無需使用 local 定義(使用也沒問題)。
腳本可使用 return 返回,也可使用 exit 命令。exit 命令用法和 return 差很少,若是不加參數則返回 0。但在代碼的任何地方,調用 exit 命令即退出腳本,即便是在一個嵌套很深的函數裏邊理調用的。
有時咱們寫的腳本須要支持比較複雜的命令行選項,好比 demo -i aa -t bb -cx ccc ddd,這樣的話,手動處理就會很麻煩。可使用內置的 getopts 命令。
#!/bin/zsh
# i: 表明能夠接受一個帶參數的 -i 選項
# c 表明能夠接受一個不帶參數的 -c 選項
while {getopts i:t:cv arg} {
case $arg {
(i)
# $OPTARG 存放選項對應的參數
echo $arg option with arg: $OPTARG
;;
(t)
echo $arg option with arg: $OPTARG
;;
(c)
echo $arg option
;;
(v)
echo version: 0.1
;;
(?)
echo error
return 1
;;
}
}
# $OPTIND 指向剩下的第一個未處理的參數
echo $*[$OPTIND,-1]
# 或者用 shift 把以前用過的參數移走
# shift $((OPTIND - 1))
# echo $*複製代碼
運行結果:
% ./demo -i aaa -t bbb -cv ccc ddd
i option with arg: aaa
t option with arg: bbb
c option
version: 0.1
ccc ddd
# 能夠只加部分選項
% ./demo -i aaa -v bbb ccc
i option with arg: aaa
version: 0.1
bbb ccc
# 能夠一個選項也不加
% ./demo aaa bbb
aaa bbb
# 若是選項不帶參數,多個選項能夠合併到一個 - 後
% ./demo -i aaa -cv bbb ccc
i option with arg: aaa
c option
version: 0.1
bbb ccc
# 若是該帶參數的選項不帶參數,會報錯
% ./demo -i aaa -t
i option with arg: aaa
./demo:3: argument expected after -t option
error
# 加了不支持的選項也會報錯
% ./demo -i aaa -a bbb ccc
i option with arg: aaa
./demo:3: bad option: -a
error
# 若是該帶參數的選項不帶參數,而後後邊緊接着另外一個選項,那麼選項會被看成參數
% ./demo -i -c aaa bbb
i option with arg: -c
aaa bbb複製代碼
getopts 的使用仍是很方便的,但它不支持長選項(如 --log aaa)。若是須要使用長選項,能夠用 getopt 命令,它是一個外部命令,能夠 man getopt 查看用法。
本文簡單介紹了函數和腳本的寫法,重點是參數處理和返回值等等,還有不少沒覆蓋的地方,之後可能繼續補充。
全系列文章地址:github.com/goreliu/zsh…
付費解決 Windows、Linux、Shell、C、C++、AHK、Python、JavaScript、Lua 等領域相關問題,靈活訂價,歡迎諮詢,微信 ly50247。