Shell函數返回值(return關鍵字)

2020-07-16 10:04:47
在 C++、Java、C#、Python 等大部分程式語言中,返回值是指函數被呼叫之後,執行函數體中的程式碼所得到的結果,這個結果就通過 return 語句返回。

但是 Shell 中的返回值表示的是函數的退出狀態:返回值為 0 表示函數執行成功了,返回值為非 0 表示函數執行失敗(出錯)了。if、while、for 等語句都是根據函數的退出狀態來判斷條件是否成立。

Shell 函數的返回值只能是一個介於 0~255 之間的整數,其中只有 0 表示成功,其它值都表示失敗。

函數執行失敗時,可以根據返回值(退出狀態)來判斷具體出現了什麼錯誤,比如一個開啟檔案的函數,我們可以指定 1 表示檔案不存在,2 表示檔案沒有讀取許可權,3 表示檔案型別不對。

如果函數體中沒有 return 語句,那麼使用預設的退出狀態,也就是最後一條命令的退出狀態。如果這就是你想要的,那麼更加嚴謹的寫法為:

return $?

$?是一個特殊變數,用來獲取上一個命令的退出狀態,或者上一個函數的返回值,請猛擊《Shell $?》了解更多。

如何得到函數的處理結果?

有人可能會疑惑,既然 return 表示退出狀態,那麼該如何得到函數的處理結果呢?比如,我定義了一個函數,計算從 m 加到 n 的和,最終得到的結果該如何返回呢?

這個問題有兩種解決方案:
  • 一種是借助全域性變數,將得到的結果賦值給全域性變數;
  • 一種是在函數內部使用 echo、printf 命令將結果輸出,在函數外部使用$()或者``捕獲結果。

下面我們具體來定義一個函數 getsum,計算從 m 加到 n 的和,並使用以上兩種解決方案。

【範例1】將函數處理結果賦值給一個全域性變數。
#!/bin/bash

sum=0  #全域性變數

function getsum(){
    for((i=$1; i<=$2; i++)); do
        ((sum+=i))  #改變全域性變數
    done

    return $?  #返回上一條命令的退出狀態
}

read m
read n

if getsum $m $n; then
    echo "The sum is $sum"  #輸出全域性變數
else
    echo "Error!"
fi
執行結果:
1
100
The sum is 5050

這種方案的弊端是:定義函數的同時還得額外定義一個全域性變數,如果我們僅僅知道函數的名字,但是不知道全域性變數的名字,那麼也是無法獲取結果的。

【範例2】在函數內部使用 echo 輸出結果。
#!/bin/bash

function getsum(){
    local sum=0  #區域性變數
    for((i=$1; i<=$2; i++)); do
        ((sum+=i))
    done
   
    echo $sum
    return $?
}

read m
read n

total=$(getsum $m $n)
echo "The sum is $total"

#也可以省略 total 變數,直接寫成下面的形式
#echo "The sum is "$(getsum $m $n)
執行結果:
1↙
100↙
The sum is 5050

程式碼中總共執行了兩次 echo 命令,但是卻只輸出一次,這是因為$()捕獲了第一個 echo 的輸出結果,它並沒有真正輸出到終端上。除了$(),你也可以使用``來捕獲 echo 的輸出結果,請猛擊《Shell變數》了解兩者的區別。

這種方案的弊端是:如果不使用$(),而是直接呼叫函數,那麼就會將結果直接輸出到終端上,不過這貌似也無所謂,所以我推薦這種方案。

總起來說,雖然C語言、C++、Java 等其它程式語言中的返回值用起來更加方便,但是 Shell 中的返回值有它獨特的用途,所以不要帶著傳統的程式設計思維來看待 Shell 函數的返回值。