Zsh 開發(fā)指南(第九篇 函數(shù)和腳本)

導(dǎo)讀

很多時(shí)候曙砂,我們寫的代碼并不是只運(yùn)行一次就不再用了艇拍,那就需要保存到文件里狐蜕。我們通常稱包含解釋性編程語言代碼的可執(zhí)行文件為腳本文件,簡(jiǎn)稱腳本卸夕。而在腳本內(nèi)部层释,也會(huì)有一些可以復(fù)用的代碼,我們可以把這樣的代碼寫成函數(shù)快集,供其他部分調(diào)用贡羔。Zsh 中函數(shù)和腳本基本上一樣的,可以認(rèn)為腳本就是以文件名為函數(shù)名的函數(shù)个初。腳本和函數(shù)的編寫方法基本相同乖寒,所以在一起講。

先從函數(shù)開始院溺,因?yàn)樯婕案俚募?xì)節(jié)楣嘁。

函數(shù)定義

# 一個(gè)很簡(jiǎn)單的函數(shù)
fun() {
    echo good
}

# 也可以在前邊加一個(gè) function 關(guān)鍵字
function fun() {
    echo good
}

這樣就可以定義一個(gè)函數(shù)了。小括號(hào)一定是空的覆获,即使函數(shù)有參數(shù)马澈,也無需在里邊寫參數(shù)列表。

直接輸入函數(shù)名即可調(diào)用函數(shù)弄息。

fun() {
    echo good
}

% fun
good

用 unfunction 可以刪除函數(shù)痊班。

fun() {
    echo good
}

% unfunction fun
% fun
zsh: command not found: fun

參數(shù)處理

函數(shù)可以有參數(shù),但 zsh 中無需顯式注明有幾個(gè)參數(shù)摹量,直接讀取即可涤伐。

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 個(gè)參數(shù),$# 是參數(shù)個(gè)數(shù)缨称。如果讀取的時(shí)候沒有對(duì)應(yīng)參數(shù)傳進(jìn)來凝果,那和讀取一個(gè)未定義的變量效果是一樣的。函數(shù)的參數(shù)只能是字符串類型睦尽,如果把整數(shù)器净、浮點(diǎn)數(shù)傳進(jìn)函數(shù)里,也會(huì)被轉(zhuǎn)成字符串当凡∩胶Γ可以把數(shù)組傳給函數(shù),然后數(shù)組中的元素會(huì)依次成為各個(gè)參數(shù)沿量。

fun() {
    echo $1 $2 $3
    echo $#
}

% array=(11 22 33)
% fun $array
11 22 33
3

這樣用的好處是可以更方便地處理帶空格的參數(shù)浪慌。

# 遍歷所有參數(shù),$* 是包含所有參數(shù)的數(shù)組
fun() {
    for i ($*) {
        echo $i
    }
}

% fun a b c
a
b
c

可以用 $+n 快速判斷第 n 個(gè)參數(shù)是否存在朴则。

fun() {
    (($+1)) && {
        echo $1
    }
}

關(guān)于 $* 和 $@权纤。在 bash 中, $* 和 $@ 的區(qū)別是一個(gè)比較麻煩的事情,但在 zsh 中汹想,通常沒有必要使用 $@外邓,所以不用踩這個(gè)坑。Bash 中需要使用 $@ 的原因是如果使用 $* 并且參數(shù)中有空格的話古掏,就分不清哪些空格是參數(shù)里的坐榆,哪些空格是參數(shù)之間的間隔符(bash 里的 $* 是一個(gè)字符串)。而如果使用 "$*" 的話冗茸,所有的參數(shù)都合并成一個(gè)字符串了席镀。而 "$@" 可以保留參數(shù)中的空格,所以通常使用 "$@"夏漱。但是有些時(shí)候需要把所有參數(shù)拼接成一個(gè)字符串豪诲,那么又要使用 "$*",所以很混亂挂绰。

而 zsh 中的 $* 會(huì)包括參數(shù)中的空格(zsh 里的 $* 是一個(gè)數(shù)組)屎篱,所以效果和 bash 的 "$@" 是差不多的。另外在 zsh 中用 "$*" 和在 bash 中的 "$*" 效果一樣葵蒂,所以只用 $* 和 "$*" 就足夠了交播。

函數(shù)嵌套

函數(shù)可以嵌套定義。

fun() {
    fun2() {
        echo $2
    }

    fun2 $1 $2
}

% fun aa bb
bb

fun2 函數(shù)是在 fun 執(zhí)行過才會(huì)被定義的践付,但最外邊也能直接訪問 fun2 函數(shù)秦士。如果想要最外邊訪問不了,可以在 fun 結(jié)束前調(diào)用 unfunction fun2 刪除 fun2 函數(shù)永高。

返回值

函數(shù)需要返回一個(gè)代表函數(shù)是否正確執(zhí)行的返回值隧土,如果是 0,代表正確執(zhí)行命爬,如果不是 0曹傀,代表有錯(cuò)誤。

#!/bin/zsh

fun() {
    (($+1)) && {
        return
    }

    return 1
}

% fun 111 && echo good
good
% fun || echo bad
bad

% fun
# 也可以用 $? 獲取函數(shù)返回值
% echo $?

遇到 return 后饲宛,函數(shù)立即結(jié)束皆愉。return 即 return 0。

注意返回值不是用來返回?cái)?shù)據(jù)的艇抠,如果函數(shù)需要將字符串幕庐、整數(shù)、浮點(diǎn)數(shù)等返回給調(diào)用者练链,直接用 echo 或者 print 等命令輸出即可翔脱,然后調(diào)用者用 $(fun) 獲取奴拦。如果需要返回?cái)?shù)組或者哈希表媒鼓,只能通過變量(全局變量或者函數(shù)所在層次的局部變量)傳遞。

fun() {
    echo 123.456
}

% echo $($(fun) *2))
246.91200000000001

通過全局變量返回。

array=()
fun() {
    array=(aa bb)
}

% fun
% echo $array
aa bb

局部變量

在函數(shù)中可以直接讀寫函數(shù)外邊的變量绿鸣,并且在函數(shù)中定義的新變量在函數(shù)退出后依然存在疚沐。

str1=abcd

fun() {
    echo $str1
    str2=1234
}

% fun
abcd
% echo $str2
1234

這通常是不符合預(yù)期的。為了避免函數(shù)內(nèi)的變量“滲透”到函數(shù)外潮模,可以使用局部變量亮蛔,使用 local 定義變量。

str1=abcd

fun() {
    echo $str1
    local str2=1234
}

% fun
abcd
% echo $str2

函數(shù)中的變量擎厢,除非確實(shí)需要留給外部使用究流,不然最好全部使用局部變量,避免引發(fā) bug动遭。

腳本

可以認(rèn)為腳本也是一個(gè)函數(shù)芬探,但它是單獨(dú)寫到一個(gè)文件里的。

test.zsh 內(nèi)容厘惦。

#!/bin/zsh

echo good

這是一個(gè)非常簡(jiǎn)單的腳本文件偷仿。第一行是固定的,供系統(tǒng)找到 zsh 解釋器宵蕉,#! 后加 zsh 的絕對(duì)路徑即可酝静。如果需要使用環(huán)境變量訪問,可以用 #!/bin/env zsh (或者 !/usr/bin/env zsh羡玛,如果 env 在 /usr/bin/ 里邊)别智。

從第二行開始,就和函數(shù)中的內(nèi)容一樣了稼稿。上邊函數(shù)體里的內(nèi)容(去掉首尾行的 fun() { 和 }亿遂,都可以寫在這里邊。

執(zhí)行的話渺杉,在 test.zsh 所在目錄蛇数,運(yùn)行 zsh test.zsh 加參數(shù)即可(就像調(diào)用了一個(gè)名為 zsh test.zsh 的函數(shù)。也可以 chmod u+x test.zsh 給它添加可執(zhí)行權(quán)限后是越,直接運(yùn)行 ./test.zsh 加參數(shù)耳舅。

腳本的參數(shù)和返回值的處理方法,和函數(shù)的完全一樣倚评,這里就不舉例了浦徊。

但函數(shù)和腳本中執(zhí)行的時(shí)候是有區(qū)別的,函數(shù)是在當(dāng)前的 zsh 進(jìn)程里執(zhí)行(也可以調(diào)用的時(shí)候加小括號(hào)在子進(jìn)程執(zhí)行)天梧,而腳本是在新的子進(jìn)程里執(zhí)行盔性,執(zhí)行完子進(jìn)程即退出了,所以腳本中的變量值外界是訪問不到的呢岗,無需使用 local 定義(使用也沒問題)冕香。

exit 命令

腳本可以使用 return 返回蛹尝,也可以使用 exit 命令。exit 命令用法和 return 差不多悉尾,如果不加參數(shù)則返回 0突那。但在代碼的任何地方,調(diào)用 exit 命令即退出腳本构眯,即使是在一個(gè)嵌套很深的函數(shù)里邊理調(diào)用的愕难。

用 getopts 命令處理命令行選項(xiàng)

有時(shí)我們寫的腳本需要支持比較復(fù)雜的命令行選項(xiàng),比如 demo -i aa -t bb -cx ccc ddd惫霸,這樣的話猫缭,手動(dòng)處理就會(huì)很麻煩∫嫉辏可以使用內(nèi)置的 getopts 命令饵骨。

#!/bin/zsh

# i: 代表可以接受一個(gè)帶參數(shù)的 -i 選項(xiàng)
# c 代表可以接受一個(gè)不帶參數(shù)的 -c 選項(xiàng)
while {getopts i:t:cv arg} {
    case $arg {
        (i)
        # $OPTARG 存放選項(xiàng)對(duì)應(yīng)的參數(shù)
        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 指向剩下的第一個(gè)未處理的參數(shù)
echo $*[$OPTIND,-1]

運(yùn)行結(jié)果:

% ./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

# 可以只加部分選項(xiàng)
% ./demo -i aaa -v bbb ccc
i option with arg: aaa
version: 0.1
bbb ccc

# 可以一個(gè)選項(xiàng)也不加
% ./demo aaa bbb
aaa bbb

# 如果選項(xiàng)不帶參數(shù),多個(gè)選項(xiàng)可以合并到一個(gè) - 后
% ./demo -i aaa -cv bbb ccc
i option with arg: aaa
c option
version: 0.1
bbb ccc

# 如果該帶參數(shù)的選項(xiàng)不帶參數(shù)茫打,會(huì)報(bào)錯(cuò)
% ./demo -i aaa -t
i option with arg: aaa
./demo:3: argument expected after -t option
error

# 加了不支持的選項(xiàng)也會(huì)報(bào)錯(cuò)
% ./demo -i aaa -a bbb ccc
i option with arg: aaa
./demo:3: bad option: -a
error

# 如果該帶參數(shù)的選項(xiàng)不帶參數(shù)居触,然后后邊緊接著另一個(gè)選項(xiàng),那么選項(xiàng)會(huì)被當(dāng)作參數(shù)
% ./demo -i -c aaa bbb
i option with arg: -c
aaa bbb

getopts 的使用還是很方便的老赤,但它不支持長(zhǎng)選項(xiàng)(如 --log aaa)轮洋。如果需要使用長(zhǎng)選項(xiàng),可以用 getopt 命令抬旺,它是一個(gè)外部命令弊予,可以 man getopt 查看用法。

總結(jié)

本文簡(jiǎn)單介紹了函數(shù)和腳本的寫法开财,重點(diǎn)是參數(shù)處理和返回值等等汉柒,還有很多沒覆蓋的地方,以后可能繼續(xù)補(bǔ)充责鳍。

參考

https://my.oschina.net/lenglingx/blog/410565

更新歷史

20170901:增加用 $? 獲取函數(shù)返回值的內(nèi)容碾褂。

20170902:增加“用 getopts 命令處理命令行選項(xiàng)”。

本文不再更新历葛,全系列文章在此更新維護(hù):github.com/goreliu/zshguide

付費(fèi)解決 Windows正塌、Linux、Shell恤溶、C乓诽、C++、AHK咒程、Python鸠天、JavaScript、Lua 等領(lǐng)域相關(guān)問題帐姻,靈活定價(jià)稠集,歡迎咨詢奶段,微信 ly50247。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末巍杈,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子扛伍,更是在濱河造成了極大的恐慌筷畦,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件刺洒,死亡現(xiàn)場(chǎng)離奇詭異鳖宾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)逆航,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門鼎文,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人因俐,你說我怎么就攤上這事拇惋。” “怎么了抹剩?”我有些...
    開封第一講書人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵撑帖,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我澳眷,道長(zhǎng)胡嘿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任钳踊,我火速辦了婚禮衷敌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拓瞪。我一直安慰自己缴罗,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開白布祭埂。 她就那樣靜靜地躺著瞒爬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪沟堡。 梳的紋絲不亂的頭發(fā)上侧但,一...
    開封第一講書人閱讀 52,158評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音航罗,去河邊找鬼禀横。 笑死,一個(gè)胖子當(dāng)著我的面吹牛粥血,可吹牛的內(nèi)容都是我干的柏锄。 我是一名探鬼主播酿箭,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼趾娃!你這毒婦竟也來了缭嫡?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤抬闷,失蹤者是張志新(化名)和其女友劉穎妇蛀,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體笤成,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡觉吭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年狭归,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了耸携。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片忙干。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖培遵,靈堂內(nèi)的尸體忽然破棺而出浙芙,到底是詐尸還是另有隱情,我是刑警寧澤籽腕,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布茁裙,位于F島的核電站,受9級(jí)特大地震影響节仿,放射性物質(zhì)發(fā)生泄漏晤锥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一廊宪、第九天 我趴在偏房一處隱蔽的房頂上張望矾瘾。 院中可真熱鬧,春花似錦箭启、人聲如沸壕翩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽放妈。三九已至,卻和暖如春荐操,著一層夾襖步出監(jiān)牢的瞬間芜抒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工托启, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留宅倒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓屯耸,卻偏偏與公主長(zhǎng)得像拐迁,于是被迫代替她去往敵國(guó)和親蹭劈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,838評(píng)論 6 342
  • 這篇文章是手冊(cè)的中文譯版整理而來(英文看著太慢了线召,感謝前人鋪路Orz...)铺韧,vim的markdown插件和實(shí)時(shí)預(yù)...
    Himryang閱讀 7,041評(píng)論 0 20
  • 1、c語言的函數(shù)有以下特點(diǎn): (1)才源程序由函數(shù)組成缓淹,一個(gè)主函數(shù)main()+若干其他函數(shù) C程序中的函數(shù)類似文...
    滕王閣序閱讀 1,345評(píng)論 0 6
  • Lua 5.1 參考手冊(cè) by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 13,831評(píng)論 0 38
  • 3月19日是妹妹結(jié)婚的日子哈打,按照一般的習(xí)慣,今天晚上家里會(huì)擺出喜糖割卖、水果前酿、點(diǎn)心患雏、茶水等鹏溯,以待親戚朋友過來沾沾喜氣。...
    浮塵過隙閱讀 450評(píng)論 0 0