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
證明了,在沒有重複堆疊的過程下,即使是大量的堆疊,還是會如我們所想。
return
和exit
的統一
函式和
shell
有太多的相似,讓我不得不去分解一下這兩者的區別。畢竟引數,語法都是一致的,那麼兩種返回有什麼不同呢。
#!/bin/bash
case $1 in
return)
return 1
;;
exit)
exit 2
;;
*)
;;
esac
首先,有額外的收穫。
在
case
中的value
欄位,有天生的遮蔽作用。除了存在萬用字元的語法之外,所謂關鍵字都只是字元而已。
雖說如此,不過儘量避免吧,誤導可不好。
執行指令碼,並$?
檢視狀態,你會發現,return
和exit
的值並沒有什麼區別。
回顧起一句話:函式沒有返回值就會把最後一條指令執行結果進行返回
。
#!/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
做為內部的組織結構,本身就是一段程式,本身就具有中斷返回的功能。加上
shell
和function
的層級關係,導致了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}
:在字串str
的start
開始擷取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
了,就別繼續騷操作
了。
- 狀態碼
區分好exit
和return
的使用範圍,別不小心把指令碼搞掛了。
- 返回值
返回的話推薦echo
進行值顯示,有好辦法的請告訴我,目前認識不多,請見諒。
return
的話,儘快糾正,他是狀態碼
返回,只能是[0,255]
,範圍有限,容易出錯。
case
case
中的value
自帶轉義,僅限於關鍵字,不過慎用。
case $param in
,頭上選擇是變數
而不是變數名
,別不小心弄錯了,還找不到錯誤。
(嗯,沒錯,就是我,浪費了好多時間)
- 字元遍歷
${str:index:length}
,擷取字串的辦法,後續估計統一學習,現在先積累一些。
str
變數名,index
數值,length
數值,別記錯了。
${$str}
取字串長度。
- 返回
雖然有狀態碼和返回值,概念不清晰的建議雙管齊下。
先通過狀態碼
判斷場景,然後通過返回值
進行操作。
要不錯誤狀態下的echo
提示語句參與了計算出錯了怎麼辦。
可能看起來比較繁瑣,但的確是個好辦法,更是一個好習慣。