- 函數的基本含義
- 函數的定義和使用
- 數組
- 字符串處理
- 特殊的處理變量用法
一、函數基本含義:
函數:多條Shell命令組成的語句塊,實現代碼重用、模塊化編程
-
函數與Shell程序的區別:
- 函數不能獨立運行,Shell程序可以獨立運行
- 函數只能在Shell程序中運行,Shell程序可以在其他Shell程序建立的子Shell中運行
- 函數可以修改Shell中的變量,子Shell程序無法修改父Shell程序的變量
使用函數前必須對其定義,定義中需要闡明函數名和函數體
-
函數的定義:函數的名稱建議有一定含義,增加可讀性
函數定義的語法格式共有3種,效果相同- 格式1:
function f_name {
函數體
......
} - 格式2:
function f_name () {
函數體
......
} - 格式3:
f_name() {
函數體
......
}
- 格式1:
函數的使用:給出函數名,則函數名位置自動被替換為函數代碼
-
函數的定義和使用方式:
- 可以在交互式環境中直接定義函數
- 可以在腳本中定義和使用函數
- 可以把函數的定義編寫在一個腳本文件中
-
函數的返回值:
- 函數體內echo語句輸出值
- 函數體內調用命令的輸出結果
-
函數的退出狀態碼
- 默認狀態:退出狀態碼取決于函數體執行的最后一條命令的退出狀態碼
- 自定義退出狀態碼 return命令:
- return,默認狀態的退出狀態碼:return前最后執行命令的退出狀態碼
- return NUM:以指定的數字NUM(0-255)作為退出狀態碼,一般0指代成功,1-255可以自定義各類錯誤
二、函數的定義與使用詳解:
(一)函數的定義:
- 交互式環境定義
f_name () { //第一行輸至{回車
>函數體語句 //>是提示符,后面輸入函數體語句,回車換行繼續輸入
>函數體語句
> ......
>} //輸入}后回車定義本函數完畢
在腳本中定義函數
shell是解釋性語言,運行時從上至下執行語句,故函數的定義語句必須在其被調用之前函數定義文件
使用函數文件中定義的函數前,必須先執行函數定義文件,此時函數的定義將載入當前shell環境中,語法:. filename
或者source filename
(二)函數的使用
-
只需給出函數名即可調用函數
- set:查詢當前所有定義的函數,這些函數都已經載入所在shell
語法:set f_name
, f_name:函數名稱 - unset:撤銷函數的定義,被撤銷函數的定義將從shell中卸載
語法:unset f_name
,f_name:函數名稱
- set:查詢當前所有定義的函數,這些函數都已經載入所在shell
環境函數:在子進程中使用父進程定義的函數
語法:
聲明環境函數:export -f f_name
,f_name函數名稱
查看環境函數:export -f
或者declare -xf
-
函數的參數:函數可以接受參數,從而擴展函數的使用功能
- 傳遞參數給函數:在調用函數時,在函數名后直接附加以空格分隔的給定參數
語法:f_name arg1 arg2 arg3...
,f_name:函數名稱;arg1,arg2,arg3:參數 - 函數體內調用參數,使用位置變量$1, $2, $3以及一些特殊變量:$*, $@, $#
- 傳遞參數給函數:在調用函數時,在函數名后直接附加以空格分隔的給定參數
-
函數變量的作用域:
- 環境變量:在父進程、子進程均有效的變量,使用
export var_name
聲明 - 本地變量:僅在本進程范圍內有效的變量,函數內對本地變量的修改將影響到函數外
- 局部變量:僅在函數的生命周期內有效,函數調用結束則自動消失,故函數內對局部變量的修改對函數外無影響,使用
local var_name
聲明 - 當本地與局部變量名稱相同時,從函數的聲明周期角度分析,函數體內部使用局部變量運算,函數體外使用本地變量運算,兩個變量相互隔離
- 環境變量:在父進程、子進程均有效的變量,使用
-
函數的遞歸調用:函數調用自身
- 函數的遞回調用一般需要有結束條件,使子層函數的結果最終能夠返回至上層函數中
- 不同的語言的最大遞歸層數不同
- fork炸彈:不斷fork進程的無限循環,最終耗盡系統資源
- 函數實現
:(){ :|:& };:
,:冒號是函數名稱,本函數與下面的函數寫法相同
bomb() { bomb | bomb & }; bomb
,函數名為bomb - 腳本實現
#! /bin/bash ./$0|./$0&
- 函數實現
-
實驗:
漢諾塔問題:三根柱子,在一根柱子上從下往上按照大小順序摞著N片盤子。要求把圓盤從下面開始按大小順序重新擺放在另一根柱子上。并且規定,在小圓盤上不能放大圓盤,在三根柱子之間一次只能移動一個圓盤,利用函數,實現N片盤的漢諾塔的移動步驟分析:
想要把N個盤子從一個柱子移動到另一個柱子,可以分為三個步驟:(1)把N-1個盤子從柱子A移動到柱子B;(2)把第N個盤子從柱子A移動到柱子C;(3)把N-1個盤子從柱子B移動到柱子C。而步驟1和步驟3都能夠再分解為如上的三個步驟,所以漢諾塔問題是一個遞歸問題。通過遞歸函數,可以實現輸出漢諾塔的移動步驟。代碼:
read -p "please type the layer of hannuota: " num
from="A"
via="B"
to="C"
#第2步的函數實現
move () {
echo "plate:$1 $2 --> $3"
}
#實現遞歸函數
hannuota () {
# echo num:$1 from:$2 via:$3 to:$4
if [ $1 -eq 1 ]; then
move $1 $2 $4
else
#遞歸過程具體實現
hannuota $[ $1-1 ] $2 $4 $3
move $1 $2 $4
hannuota $[ $1-1 ] $3 $2 $4
fi
}
#調用函數
hannuota $num $from $via $to
執行結果如下:
三、數組:
(一)數組的定義:
數組:存儲多個元素的連續的內存空間,相當于多個變量的集合
數組的索引:從0開始編號,數字索引方式
數組的關聯索引:自定義索引,使用字母、數字等符號作為索引,從bash 4.0開始支持
稀疏數組:數組的索引編號不連續,可以創建時不連續,也可能因數組的增刪操作而變得不連續
(二)數組的語法介紹:
聲明數組:普通數組和關聯數組一經聲明不可互相轉換
普通數組:declare -a ARRAY_NAME
關聯數組:declare -A ARRAY_NAME,關聯數組必須先聲明才能被調用數組元素的賦值:
(1)給單個元素賦值:
ARRAY_NAME[#]=VALUE
(2)一次將數組元素賦值:
ARRAY_NAME=("VAL1" "VAL2" "VAL3" "VAL4" ...)
(3)給特定的數組元素賦值:
ARRAY_NAME=([0]="VAL1" [2]="VAL2" ...)
(4)交互式賦值:
read -a ARRAY_NAME顯示所有數組:
declare -a
(三)數組數據的處理:
引用數組元素:${ARRAY_NAME[#]}
注明:${ARRAY_NAME}相當于${ARRAY_NAME[0]}引用數組所有元素:
${ARRAY_NAME[*]} 或者 ${ARRAY_NAME[@]}數組的長度(數組元素的個數)
${#ARRAY_NAME[*]} 或者 ${#ARRAY_NAME[@]}添加數組元素至數組尾部
ARRAY_NAME[${#ARRAY_NAME[*]}]=VALUE刪除數組元素
unset ARRAY_NAME[#]刪除數組
unset ARRAY_NAME數組切片
${ARRAY_NAME[@]:offset:number}
offset:跳過元素的個數
number:取出元素的個數實驗:如下圖所示,實現轉置矩陣matrix.sh
1 2 3 1 4 7
4 5 6 ===> 2 5 8
7 8 9 3 6 9
分析發現需要交換的數字其特點在于,兩個數字所在位置的行號與列號正好相反。如數字2位于行1列2,而數字4位于行2列1。由此,可以分別采用兩次循環操作行號和列號,并交換符合要求的數字。為了防止已經交換的數字再次交換,實際上需要虛幻的數字位于數字1至數字9所連接成的對角線上方數字即可。
代碼如下:
#要求用戶輸入想要N*N的矩陣
read -p "please type the number of matrix: " matrix_num
#從0行0列開始數組編號,所以先對用戶輸入值自減1
let matrix_num--
num=1
#定義數組
declare -a matrix
#從1開始為矩陣賦值
for i in `seq 0 $matrix_num`; do
for j in `seq 0 $matrix_num`; do
matrix[$i$j]=$num
let num++
done
done
#定義函數,實現矩陣打印
func() {
for p in `seq 0 $matrix_num`; do
for q in `seq 0 $matrix_num`; do
echo -ne "${matrix[$p$q]}\t"
done
echo
done
}
#調用函數,打印矩陣
func
echo
# 將矩陣轉置
for x in `seq 0 $matrix_num`; do
for y in `seq $x $matrix_num`; do
if ! [ $x -eq $y ]; then
temp=${matrix[$x$y]}
matrix[$x$y]=${matrix[$y$x]}
matrix[$y$x]=$temp
fi
done
done
#再次調用func函數,打印矩陣
func
執行結果如下:
四、字符串處理:
(一)字符串切片:
${#var}:返回字符串變量的長度
${var:offset}:返回字符串從第offset個(不含第offset個)到最后字符的部分
${var:offset:num}:返回字符串從第offset個(不含第offset個)共num個字符長度的部分
${var: -length}:返回字符串最右側的length個字符,冒號后有一個空格
${var:offset:-length}:返回字符串從第offset個(不含第offset個)到距最右側共length個字符的部分
${var: -length:-offset}:從最右側向左取length個字符開始,至距最右側offset個字符之間的字符,-length前有一個空格
(二)基于模式取子字符串:
${var#*word}:刪除從字符串開始至第一次出現word之間的所有字符
${var##*word}:貪婪模式,刪除從字符串開始至最后一次出現word之間的所有字符
${var%word*}:刪除從最右側開始至第一次出現word之間的所有字符
${var%%word*}:貪婪模式,刪除從最右側開始至最后一次出現word之間的所有字符
(三)查找替換
${var/pattern/substr}:將查找到的第一次匹配pattern的字符串替換為substr
${var//pattern/substr}:將查找到的所有匹配pattern的字符串替換為substr
${var/#pattern/substr}:將行首匹配pattern的字符串替換為substr
${var/%pattern/substr}:將行尾匹配pattern的字符串替換為substr
(四)查找刪除
${var/pattern}:將查找到的第一次匹配pattern的字符串刪除
${var//pattern}:將查找到的所有匹配pattern的字符串刪除
${var/#pattern}:將行首匹配pattern的字符串刪除
${var/%pattern}:將行尾匹配pattern的字符串刪除
(五)大小寫轉換
${var^^}:字符串全部轉換為大寫
${var,,}:字符串全部轉換為小寫
五、特殊的處理變量用法:
(一)聲明為有類型變量:declare 和 typeset,兩者等價
語法:
declare [選項] 變量名
-r 聲明或顯示只讀變量
-i 將變量定義為整型數
-a 將變量定義為數組
-A 將變量定義為關聯數組
-f 顯示此腳本前定義過的所有函數名及其內容
-F 僅顯示此腳本前定義過的所有函數名
-x 聲明或顯示環境變量和函數
-l 聲明變量為小寫字母
-u 聲明變量為大寫字母
(二)eval命令:
功能:先掃描一次命令行執行全部替換,再執行命令
適用于一次掃描不能實現功能的變量,eval命令可以實現兩次掃描
(三)間接變量引用:
如果第一個變量的值是第二個變量的名字,從第一個變量引用第二個變量的值就稱為間接變量引用
實現方法:
eval tempvar=$$variable1
tempvar=${!variable1}
(四)創建臨時文件 mktemp命令:
功能:創建并顯示臨時文件,可避免沖突
語法:
mktemp [OPTION]... [TEMPLATE]
TEMPLATE: filename.XXXX,至少要出現三個
OPTION:
-d: 創建臨時目錄
-p DIR或--tmpdir=DIR:指明臨時文件所存放目錄位置
(五)安裝復制文件 install命令:
可以實現文件復制,所有者、所屬組修改,權限修改在一條命令中執行
語法:
install [OPTION]... [-T] SOURCE DEST 單文件
install [OPTION]... SOURCE... DIRECTORY
install [OPTION]... -t DIRECTORY SOURCE...
install [OPTION]... -d DIRECTORY...創建空目錄選項:
-m MODE,默認755
-o OWNER
-g GROUP
(六)expect命令:
功能:實現自動化交互式操作場景
expect 語法:
expect [選項] [ -c cmds] [ [ -[f|b] ] cmdfile] [ args ]選項
-c:從命令行執行expect腳本,expect默認交互式執行
-d:可以輸出調試信息expect中相關命令
spawn:啟動新的進程
send:用于向進程發送字符串
expect:從進程接收字符串
interact:允許用戶交互
exp_continue:匹配多個字符串在執行動作后加此命令-
expect最常用的語法(tcl語言:模式-動作)
單一分支模式語法:
expect “hi” {send “You said hi\n"}
匹配到hi后,會輸出“you said hi”,并換行多分支模式語法:
expect "hi" { send "You said hi\n" }
"hehe" { send “Hehe yourself\n" }
"bye" { send “Good bye\n" }
匹配hi,hello,bye任意字符串時,執行相應輸出。等同如下:
expect {
"hi" { send "You said hi\n"}
"hehe" { send "Hehe yourself\n"}
"bye" { send “Good bye\n"}
}