Shell模組化(source命令)

2020-07-16 10:04:45
所謂模組化,就是把程式碼分散到多個檔案或者資料夾。對於大中型專案,模組化是必須的,否則會在一個檔案中堆積成千上萬行程式碼,這簡直是一種災難。

基本上所有的程式語言都支援模組化,以達到程式碼複用的效果,比如,Java 和 Python 中有 import,C/C++ 中有 #include。在 Shell 中,我們可以使用 source 命令來實現類似的效果。

在《執行Shell指令碼》一節中我們已經提到了 source 命令,這裡我們再來講解一下。

source 命令的用法為:

source filename

也可以簡寫為:

. filename

兩種寫法的效果相同。對於第二種寫法,注意點號.和檔名中間有一個空格。

source 是 Shell 內建命令的一種,它會讀取 filename 檔案中的程式碼,並依次執行所有語句。你也可以理解為,source 命令會強制執行指令碼檔案中的全部命令,而忽略指令碼檔案的許可權。

範例

建立兩個指令碼檔案 func.sh 和 main.sh:func.sh 中包含了若干函數,main.sh 是主檔案,main.sh 中會包含 func.sh。

func.sh 檔案內容:
#計算所有引數的和
function sum(){
    local total=0

    for n in [email protected]
    do
         ((total+=n))
    done

    echo $total
    return 0
}

main.sh 檔案內容:
#!/bin/bash

source func.sh

echo $(sum 10 20 55 15)
執行 main.sh,輸出結果為:
100

source 後邊可以使用相對路徑,也可以使用絕對路徑,這裡我們使用的是相對路徑。

避免重複引入

熟悉 C/C++ 的讀者都知道,C/C++ 中的標頭檔案可以避免被重複引入;換句話說,即使被多次引入,效果也相當於一次引入。這並不是 #include 的功勞,而是我們在標頭檔案中進行了特殊處理。

Shell source 命令和 C/C++ 中的 #include 類似,都沒有避免重複引入的功能,只要你使用一次 source,它就引入一次指令碼檔案中的程式碼。

那麼,在 Shell 中究竟該如何避免重複引入呢?

我們可以在模組中額外設定一個變數,使用 if 語句來檢測這個變數是否存在,如果發現這個變數存在,就 return 出去。

這裡需要強調一下 return 關鍵字。return 在 C++、C#、Java 等大部分程式語言中只能退出函數,除此以外再無他用;但是在 Shell 中,return 除了可以退出函數,還能退出由 source 命令引入的指令碼檔案。

所謂退出指令碼檔案,就是在被 source 引入的指令碼檔案(子檔案)中,一旦遇到 return 關鍵字,後面的程式碼都不會再執行了,而是回到父指令碼檔案中繼續執行 source 命令後面的程式碼。

return 只能退出由 source 命令引入的指令碼檔案,對其它引入指令碼的方式無效。

下面我們通過一個範例來演示如何避免指令碼檔案被重複引入。本例會涉及到兩個指令碼檔案,分別是主檔案 main.sh 和 模組檔案 module.sh。

模組檔案 module.sh:
if [ -n "$__MODULE_SH__" ]; then
    return
fi
__MODULE_SH__='module.sh'

echo "http://c.biancheng.net/shell/"
注意第一行程式碼,一定要是使用雙引號把$__MODULE_SH__包圍起來,具體原因已經在《Shell test》一節中講到。

主檔案 main.sh:
#!/bin/bash

source module.sh
source module.sh

echo "here executed"
./表示當前檔案,你也可以直接寫作source module.sh

執行 main.sh,輸出結果為:
http://c.biancheng.net/shell/
here executed

我們在 main.sh 中兩次引入 module.sh,但是只執行了一次,說明第二次引入是無效的。

main.sh 中的最後一條 echo 語句產生了輸出結果,說明 return 只是退出了子檔案,對父檔案沒有影響。