Linux探索之旅|第五部分第六課:Shell 函數篇

Linux探索之旅|第五部分第六課:Shell 函數篇

內容簡介


  1. 前言

  2. 函數的作用

  3. 函數的定義

  4. 傳遞參數

  5. 返回值

  6. 變量作用範圍

  7. 重載命令

  8. 函數的設計

  9. 總結

  10. 第五部分第七課預告

1. 前言


上一課 Linux探索之旅 | 第五部分第五課:循環往復,Shell開路 中我們學習了 Shell 中的循環語句。

這一課我們來學習對於大多數編程語言來說很重要的內容:函數。

2. 函數的作用


函數到底是什麼呢?

以前在學校裡學習數學的時候,也學過函數。例如:

y = 2x + 1

這個公式也被稱為一個函數,對於每一個給定的 x 值,都會有一個 y 值被計算出來,就是 2 倍的 x ,加上 1。

因為編程的基礎是數學,因此程序中的函數也有點數學中函數的原理,給它一定輸入,它會為你產出一定的東西。

函數在英語中是 function,表示 「功能,作用」的意思。因此,你可以這麼理解:函數是實現一定功能的代碼塊。

我們可以把函數比作一個香腸製造機,在輸入那一頭你把豬裝進去,輸出那一頭就出來香腸了。

Linux探索之旅|第五部分第六課:Shell 函數篇

函數就像香腸製造機

函數還是重用代碼(reuse code)的很好方式。

為什麼這麼說呢?

因為到目前為止,我們寫的所有 Shell 文件,都是定義一些變量,然後執行一些命令,這些命令還都是逐行依次執行。

目前我們寫的例子程序都比較短小,因此看不出有什麼問題。假如這個文件的內容一多,達到好幾萬行甚至更多,而且有不少重複的內容,那麼要維護這樣一個龐雜的文件就極為困難。

這時候,函數就可以出馬幫我們解圍。因為函數可以把一塊代碼包裹起來,使之成為一個整體,完成某些任務。而且,我們在程序的其他地方,還可以多次使用這塊代碼。

這樣,我們的程序就會變得有條理,也沒有那麼多重複的相似代碼。你可以把函數想象成 Shell 腳本里的小腳本。

3. 函數的定義


說了這麼多,也許還是不太好理解,我們不如實際來使用一下函數。

如果你學過其他編程語言,比如 C語言,Java,等等,那麼其實 Shell 中的函數機理是與之類似的。但是,Shell 比較「任性」,它的函數形式和主流編程語言有不少區別。

定義(或創建) Shell 函數是非常容易的。有兩種方式:

函數名 () {

function 函數名 {
  • 函數名後面跟著的圓括號裡不加任何參數:這一點與主流編程語言很不相同。C語言,Java,C++等語言中,函數的圓括號中是可以放置參數的(也就是函數的一部分輸入),但是 Shell 中的函數的圓括號裡不能放置參數。

  • 函數的完整定義必須置於函數的調用之前。

我們通過一個小例子來加深理解:

vim function.sh

然後在裡面輸入以下內容:

#!/bin/bashprint_something () { echo Hello I am a function}

「 Hello I am a function 」是英語「 你好,我是一個函數」的意思。

運行:

Linux探索之旅|第五部分第六課:Shell 函數篇

可以看到程序打印了兩次「 Hello I am a function 」。

我們逐行來解釋這個程序:

  • 第 3 行 : 我們開始了一個函數的定義,給它起了一個名字,叫做 print_something (print 是英語 「打印」的意思,something 是英語 「某些東西」的意思)。

  • 第 4 行:在大括號 {} 中,我們可以寫入多個命令。

  • 第 7 和 8 行:在函數定義之後,我們就可以調用它任意多次。這裡連續調用了兩次。

4. 傳遞參數


我們上面的函數並沒有處理參數,有時候我們希望函數能為我們處理一些參數,然後輸出一些結果。

在 Shell 函數中,我們給它傳遞參數的方式其實很像給 Shell 腳本傳遞命令行參數。我們把參數直接置於函數名字後面,然後就像我們之前 Shell 腳本的參數那樣:$1, $2, $3, 等等。

我們來看一個例子:

#!/bin/bashprint_something () { echo Hello $1}

Linux探索之旅|第五部分第六課:Shell 函數篇

5. 返回值


大多數主流編程語言都有函數返回值的概念,可以讓函數回傳一些數據。

Shell 的函數卻沒辦法做到。但是 Shell 的函數可以返回一個狀態,有點類似一個程序或命令退出時會有一個退出狀態,表明是否成功。

Shell 函數要返回狀態,也用 return 這個關鍵字( return 是英語 「返回」的意思)。

來看一個例子:

#!/bin/bashprint_something () { echo Hello $1
  • 第 5 行:返回的狀態不一定要是被硬編碼的(比如上例中的 1 ),也可以是一個變量。

  • 第 10 行:變量 $? 包含前一次被運行的命令或函數的返回狀態。

運行:

Linux探索之旅|第五部分第六課:Shell 函數篇

一般來說,返回狀態 0 表示一切順利;一個非零值表示有錯誤。

如果你實在想要函數返回一個數值(比如說一個計算的值),那麼你也可以用命令的執行結果。

如下:

#!/bin/bashlines_in_file () {

運行:

Linux探索之旅|第五部分第六課:Shell 函數篇

6. 變量作用範圍


變量的作用範圍意味著一個 Shell 腳本的哪些部分可以訪問到這個變量。

默認來說,一個變量是全局的(global),意味著在腳本的任何地方都可以訪問它。

我們也可以創建局部(local)變量。當我們在函數中創建局部變量時,這個變量就只能在這個函數中被訪問。

要定義一個局部變量,我們只要在第一次給這個變量賦值時在變量名前加上關鍵字 local 即可( local 是英語「 本地的」的意思)。

定義局部變量有一個好處,就是可以防止被腳本的其他地方的代碼意外改變數值。

來看一個例子:

#!/bin/bashlocal_global () { local var1='local 1'

運行:

Linux探索之旅|第五部分第六課:Shell 函數篇

在函數中,儘量用局部變量。只有實在不行才用全局變量,畢竟全局變量不太安全。

7. 重載命令


我們可以用函數來實現命令的重載,也就是說把函數的名字取成與我們通常在命令行用的命令相同的名字。

例如,也許我們每次在腳本中調用 ls 命令時,其實是想要實現 ls -lh 的效果。那麼我們可以這麼做:

#!/bin/bashls () { command ls -lh

第 4 行如果沒有 command 這個關鍵字(command 是英語 「命令」的意思),那麼程序會陷入無限循環。

如果你不小心忘了 command 關鍵字而陷入無限循環,可以用 Ctrl + c 的組合快捷鍵來停止程序。

運行:

Linux探索之旅|第五部分第六課:Shell 函數篇

8. 函數的設計


我們已經看到:在 Shell 中定義函數是很簡單的。但是要定義容易維護和易於擴展的函數卻需要經驗和時間。

有時候,好的設計意味著更少的代碼;有時候意味著需求變更時最少的改動;有時候意味著不容易引起錯誤。

如果一個任務需要被執行多次,那麼把它放到一個函數里是不錯的選擇。

設計模式中有一個原則是 單一職責原則 ( Single Responsibility Principle )。這種原則也適用於函數的設計,儘量不要讓一個函數執行很多個任務。

9. 總結


  1. 函數使我們可以輕鬆地重用代碼,使程序更有條理,更易理解和擴展。

  2. 我們有兩種定義函數的方式:「 function 函數名 」 或 「 函數名 () 」。

  3. 關鍵字 return 可以用於返回狀態值。

  4. 關鍵字 local 可以用於定義局部變量。

  5. 關鍵字 command 使得函數可以重載命令。

10. 第五部分第七課預告


今天的課就到這裡,一起加油吧!

下一課我們學習:小練習:圖片展示程序

我是[謝恩銘](http://www.jianshu.com/u/44339a8a9afa),在巴黎奮鬥的軟件工程師。

[我的簡介](http://www.jianshu.com/p/e1c5835fee7d)

[我的經歷](http://www.jianshu.com/p/86c2cfe3b390)

熱愛生活,喜歡游泳,略懂烹飪。

人生格言:“向著標杆直跑”


分享到:


相關文章: