20170829 Shell編程進階(二)

  • 函數的基本含義
  • 函數的定義和使用
  • 數組
  • 字符串處理
  • 特殊的處理變量用法

一、函數基本含義:

  • 函數:多條Shell命令組成的語句塊,實現代碼重用、模塊化編程

  • 函數與Shell程序的區別:

    • 函數不能獨立運行,Shell程序可以獨立運行
    • 函數只能在Shell程序中運行,Shell程序可以在其他Shell程序建立的子Shell中運行
    • 函數可以修改Shell中的變量,子Shell程序無法修改父Shell程序的變量
  • 使用函數前必須對其定義,定義中需要闡明函數名和函數體

  • 函數的定義:函數的名稱建議有一定含義,增加可讀性
    函數定義的語法格式共有3種,效果相同

    • 格式1:
      function f_name {
      函數體
      ......
      }
    • 格式2:
      function f_name () {
      函數體
      ......
      }
    • 格式3:
      f_name() {
      函數體
      ......
      }
  • 函數的使用:給出函數名,則函數名位置自動被替換為函數代碼

  • 函數的定義和使用方式:

    • 可以在交互式環境中直接定義函數
    • 可以在腳本中定義和使用函數
    • 可以把函數的定義編寫在一個腳本文件中
  • 函數的返回值:

    • 函數體內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:函數名稱
  • 環境函數:在子進程中使用父進程定義的函數
    語法:
    聲明環境函數: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"}
      }

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,237評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,957評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 177,248評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,356評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,081評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,485評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,534評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,720評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,263評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,025評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,204評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,787評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,461評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,874評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,105評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,945評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,205評論 2 375

推薦閱讀更多精彩內容