1. 程式人生 > >linux-function

linux-function

結構

function func_name(){
    statement
    ...
    return code
}

架子和js的看起來一樣,不過深究一下,差異還是特別的多。

position description
function 函式宣告,可省略
func_name 函式名稱,不可省略
() 函式標記,不可帶參
statement shell
語法
return 返回狀態
param 引數和shell一樣,使用$n進行提取

使用$?提取return的值

狀態碼和返回值

狀態碼的探究

  • 斐波那契引發的思考

寫個斐波那契感受一下

#!/bin/bash
function fb(){
    if [ $1 -eq 1] || [ $1 -eq 2 ];then
        return 1
    else
        fb $(($1 - 1))
        last1=
$? fb $(($1 - 2)) last2=$? return $(($last1 + $last2)) fi } fb $1 echo $?

你會發現,5以上的結果都錯了。換個方式

#!/bin/bash
function fb(){
    if [ $1 -eq 2 ] || [ $1 -eq 1 ];then
        echo 1
    else
        last1=`fb $(($1 - 1))`
        last2=`fb $(($1 - 2))`
        echo $((last1 +
last2))
fi } fb $1

這下結果正確了,可以說,我們的邏輯沒有問題,問題出在哪呢?

  • 累加和的矯正
#!/bin/bash
function sum(){
    if [ $1 -eq 1 ];then
        return 1
    else
        sum $(($1 - 1))
        last_sum=$?
        return $(($1 + $last_sum))
    fi
}
sum $1
echo $?

可以看到,在一定範圍內,我們的計算結果是正確的。

查閱一下,思考一下。

$?獲取的是最後一條命令的返回狀態。

狀態碼範圍是[0,255],由此,我們的sum計算結果超出255就註定了溢位被截斷。

fb中,遞迴巢狀太多了,其中的堆疊邏輯我也不好確認。

列印計算步驟之後,發現最後一步總是+1。

也就是說,$?所謂的最後一步,並不是我們感官那樣的最後一步。

不停的堆疊巢狀,加上fb中的重複計算,我們最終是丟失了我們的邏輯。

不過sum證明了,在沒有重複堆疊的過程下,即使是大量的堆疊,還是會如我們所想。

  • returnexit的統一

函式和shell有太多的相似,讓我不得不去分解一下這兩者的區別。

畢竟引數,語法都是一致的,那麼兩種返回有什麼不同呢。

#!/bin/bash
case $1 in 
return)
    return 1
    ;;
exit)
    exit 2
    ;;
*)
    ;;
esac

首先,有額外的收穫。

case中的value欄位,有天生的遮蔽作用。

除了存在萬用字元的語法之外,所謂關鍵字都只是字元而已。

雖說如此,不過儘量避免吧,誤導可不好。

執行指令碼,並$?檢視狀態,你會發現,returnexit的值並沒有什麼區別。

回顧起一句話:函式沒有返回值就會把最後一條指令執行結果進行返回

#!/bin/bash
function show(){
    ls godme.sh
}
show

godme.sh這個檔案是不存在的。

你會發現,$?是127,這個又是從哪裡來的。

狀態碼的層級

自不量力,強行理解一波

  • 命令

命令就是可執行的東東,大致場景就在於編寫和呼叫兩個地方。

如此劃分,ls之類的算作命令,函式也是命令,指令碼也是一種命令。

可執行的都算作是命令,可呼叫,也可以自己編寫。

  • 返回

每個命令呢,都有返回值,但是僅僅是自身的返回值。

其他被呼叫的命令的返回值,不能夠決定當前命令的返回值。

不過預設預設最後一條被呼叫命令返回值作為此命令的返回值。

  • 流程

說實話,就是一箇中斷返回的優先順序問題。

型別 作用域 效果
exit shell 中斷指令碼並返回
return function 中斷函式並返回
other shell/function 中斷自身並返回
#!/bin/bash
function test(){
    exit 4
}
test
exit 3

這個指令碼執行之後,返回的必定是4,因為指令碼已經中斷並返回了。

#!/bin/bash
function test(){
    return 4
}
test
exit 3

這個呢,中斷返回的是3,指令碼的事情,和函式的return有什麼關係呢。

#!/bin/bash
function test(){
    return 4
}
test

恩,預設預設,返回值當然是4啦。

簡單說來,進行程式設計的時候,我們只涉及了邏輯的中斷返回,並不管理程式的生命週期。

但是在shell中,shell作為可直接呼叫的程式,本身就可以中斷。

function做為內部的組織結構,本身就是一段程式,本身就具有中斷返回的功能。

加上shellfunction的層級關係,導致了exit可以在function中使用,才有這麼奇妙而畸形的組合。

更加說明了程式碼塊這個概念,避免重複編寫,而直接引用的好處。

不過,外部邏輯影響了內部邏輯,的確是個大問題,如果是非當前指令碼的函式中的exit導致指令碼中斷。

這問題可不小。

不過目前學的淺,不知道指令碼能否呼叫外部函式。

但是在編寫函式的時候,還是要注意別使用exit,有需求的話請再三確認。

返回值的概念

前面雖然一直說是返回值,只是為了穩住return的概念,實際上應該叫做狀態碼

那麼,真正的返回值到底是什麼呢。

說實話,我目前也不太清楚,但是$?的確是太過於繁瑣了。

我個人更傾向於使用****當做返回值,也就是直接用echo``進行輸出計算。

#!/bin/bash
function sum(){
    if [ $1 -eq 1 ];then
        echo 1
    else
        echo $(($1 + `sum $(($1 - 1))`))
    fi
}
sum $1

兩者的統一

高階語言中,我們經常會遇見一個問題:如果是XX結果,我們需要NN。

不過尷尬的是,我們的返回值只能有一個。

當然java當中可以用陣列,python中用元組。

最方便的就是用c++了,一個指標引用,內部修改就能夠傳達到外部。

這聽起來像是if...else,不過,貼切點來說應該是try...catch

針對不同的執行結果,我們採取不同的處理辦法。

if...else針對的是邏輯結構,更寬泛一些。

try...catch針對的是執行結果,更有針對性一些。

型別 說明
返回碼 具體場景
返回值 具體結果

如果一開始就拿到了exception,不就是if...else了麼。

  • 遍歷字串

為了演示一下功用,必須寫一個指令碼試試,首先先介紹一個字串遍歷的辦法。

#!/bin/bash
str=$1
for index in `seq ${#str}`;do
    echo ${str:$index-1:1}
done
  • seq:這個沒忘記吧,可以回顧一下
  • #:東西除了可以是註釋,語法當中它可以用來計算長度
  • ${}:包括一個表示式而已
  • ${str:start:length}:在字串strstart開始擷取length個字元
  • 指令碼演示

得到一個字串,計算其中數值的和,拼接其他非數值字串,並區分大小寫和特殊字元

#!/bin/bash

function getChar(){
echo $1
case $1 in
[0-9])
    return 1
    ;;
[a-z])
    return 2
    ;;
[A-Z])
    return 3
    ;;
*)
    return 4
    ;;
esac
}
str=$1
sum=0
lowercase=""
uppercase=""
others=""
for index in `seq ${#str}`;do
    value=`getChar ${str:$index-1:1}`
    status=$?
    case $status in
    1)
        ((sum+=value))
        ;;
    2)
        lowercase=$lowercase$value
        ;;
    3)
        uppercase=$uppercase$value
        ;;
    4)
        others=$others$value
        ;;
    esac
done
echo -e "sum\t\t: $sum"
echo -e "lowercase\t: $lowercase"
echo -e "uppercase\t: $uppercase"
echo -e "others\t\t: $others"

通過程式碼來看,總是感覺不夠明晰。

但是,退出狀態這一概念,的確是明白的顯露出來的。

至於通過狀態選擇分支,和通過條件選擇分支,真就是一個模子。

我也無法說的更清楚一些。

指令碼

  • 連加

補上之前欠下的,多引數連加

#!/bin/bash
function sum(){
    if [ $# -le 0 ];then
        echo "need param"
    fi
    result=0
    while [ $# -gt 0 ];do
        ((result+=$1))
        shift
    done
    echo $result
}
sum $*

遞迴的也可以

#!/bin/bash
function sum(){
    if [ $# -eq 2 ];then
        echo $(($2+$1))
    else
        head=$1
        shift
        echo $((head+`sum $*`))
    fi
}
sum $*

陣列遍歷的話,後面學到再說,要不知識體系有點混亂了,東一槍西一槍的。

小結

  • 遞迴

命令都可以遞迴使用,之前沒有函式的時候,我們遞迴的是指令碼自身

現在又function了,就別繼續騷操作了。

  • 狀態碼

區分好exitreturn的使用範圍,別不小心把指令碼搞掛了。

  • 返回值

返回的話推薦echo進行值顯示,有好辦法的請告訴我,目前認識不多,請見諒。

return的話,儘快糾正,他是狀態碼返回,只能是[0,255],範圍有限,容易出錯。

  • case

case中的value自帶轉義,僅限於關鍵字,不過慎用。

case $param in,頭上選擇是變數而不是變數名,別不小心弄錯了,還找不到錯誤。

(嗯,沒錯,就是我,浪費了好多時間)

  • 字元遍歷

${str:index:length},擷取字串的辦法,後續估計統一學習,現在先積累一些。

str變數名,index數值,length數值,別記錯了。

${$str}取字串長度。

  • 返回

雖然有狀態碼和返回值,概念不清晰的建議雙管齊下。

先通過狀態碼判斷場景,然後通過返回值進行操作。

要不錯誤狀態下的echo提示語句參與了計算出錯了怎麼辦。

可能看起來比較繁瑣,但的確是個好辦法,更是一個好習慣。