流程控制
-
過程式編程語言:
-
順序執行
- 如果是命令寫錯了,可以繼續往下執行;但當語法錯誤時則不會往下繼續執行;
- 選擇執行
- 循環執行
-
順序執行
條件選擇:if語句
-
單分支
if 判斷條件;then 條件為真的分支代碼 fi
-
雙分支
if 判斷條件; then 條件為真的分支代碼 else 條件為假的分支代碼 fi
-
多分支
if 判斷條件1; then 條件為真的分支代碼 elif 判斷條件2; then 條件為真的分支代碼 elif 判斷條件3; then 條件為真的分支代碼 else 以上條件都為假的分支代碼 fi
-
逐條件進行判斷,第一次遇為“真”條件時,執行其分支,
而后結束整個if語句
條件判斷:case語句
-
case支持glob風格的通配符:
-
*: 任意長度任意字符
?: 任意單個字符
[]:指定范圍內的任意單個字符
a|b: a或b
case $VARAIBLE in PAT1) 分支1 ;; PAT2) 分支2 ;; ... *) 默認分支 ;; esac
-
*: 任意長度任意字符
循環
-
循環執行
-
將某代碼段重復運行多次
重復運行多少次:-
循環次數事先已知
循環次數事先未知
-
循環次數事先已知
-
有進入條件和退出條件
-
- for,while,until
for循環
for 變量名 in 列表;do 循環體 done
-
執行機制:
依次將列表中的元素賦值給“變量名”; 每次賦值后即執行一次循環體; 直到列表中的元素耗盡,循環結束 -
列表生成方式:
- 直接給出列表
-
整數列表:
- {start..end}
- $(seq [start [step]] end)
-
~]# sum=10
~]# eval echo {1..$sum}
1 2 3 4 5 6 7 8 9 10
-
返回列表的命令
$(COMMAND)
反引號 - 使用glob,如:*.sh
-
變量引用;
$@, $*
-
特殊用法
-
雙小括號方法,即((…))格式,也可以用于算術運算
雙小括號方法也可以使bash Shell實現C語言風格的變量操作
#I=10
#((I++)) -
for循環的特殊格式:
for ((控制變量初始化;條件判斷表達式;控制變量的修正表達式)) do 循環體 done 控制變量初始化:僅在循環代碼開始運行時執行一次 控制變量的修正表達式:每輪循環結束會先進行控制變量修正運算,而后再做條件判斷
-
while循環
while CONDITION; do 循環體 done
-
CONDITION:循環控制條件;進入循環之前,先做一次判斷;每一次循環之后會再次做判斷;條件為“true”,則執行一次循環;直到條件測試狀態為“false”終止循環
-
因此:CONDTION一般應該有循環控制變量;而此變量的值會在循環體不斷地被修正
-
進入條件:CONDITION為true
退出條件:CONDITION為false -
特殊用法
while循環的特殊用法(遍歷文件的每一行):while read line; do 循環體 done < /PATH/FROM/SOMEFILE
依次讀取/PATH/FROM/SOMEFILE文件中的每一行,且將行賦值給變量line
-
死循環
while ture;do 循環體 done
- 退出方式:某個測試條件滿足時,讓循環體執行break命令;
until循環
until CONDITION; do 循環體 done
-
進入條件: CONDITION 為false
退出條件: CONDITION 為true
循環控制語句continue
-
用于循環體中
-
continue [N]:提前結束 [第N層] 本輪循環,而直接進入下一輪判斷;最內層為第1層;結束continue [N] 所在的循環
while CONDTIITON1; do CMD1 ... if CONDITION2; then continue fi CMDn ... done
循環控制語句break
-
用于循環體中
-
break [N]:提前結束 [第N層] 循環,最內層為第1層;結束整個循環
while CONDTIITON1; do CMD1 ... if CONDITION2; then break fi CMDn ... done
循環控制shift命令
-
shift [n]
-
用于將參量列表 list 左移指定次數,缺省為左移一次。
參量列表 list 一旦被移動,最左端的那個參數就從列表中刪除。 -
while 循環遍歷位置變量列表時,常用到 shift
-
./doit.sh a b c d e f g h
./shfit.sh a b c d e f g h -
示例:
#!/bin/bash while [ $# -gt 0 ] # or (( $# > 0 )) do echo $* shift done
#!/bin/bash #step through all the positional parameters until [ -z "$1" ] do echo "$1" shift done echo
select循環與菜單
select variable in list do 循環體命令 done
-
select 循環主要用于創建菜單,按數字順序排列的菜單項將顯示在標準錯誤上,并顯示 PS3 提示符,等待用戶輸入
-
用戶輸入菜單列表中的某個數字,執行相應的命令
用戶輸入被保存在內置變量 REPLY 中
-
用戶輸入菜單列表中的某個數字,執行相應的命令
-
select與case
- select 是個無限循環,因此要記住用 break 命令退出循環,或用 exit 命令終止腳本。也可以按 ctrl+c退出循環
- select 經常和 case 聯合使用
- 與 for 循環類似,可以省略 in list,此時使用位置參量
函數
函數介紹
-
函數function是由若干條shell命令組成的語句塊,實現代碼重用和模塊化編程
-
它與shell程序形式上是相似的,不同的是它不是一個單獨的進程,不能獨立運行,而是shell程序的一部分
-
函數和shell程序比較相似,區別在于:
- Shell程序在子Shell中運行
- 而Shell函數在當前Shell中運行。因此在當前Shell中,函數可以對shell中變量進行修改
定義函數
help function
函數由兩部分組成:函數名和函數體
語法一: function f_name { ...函數體... } 語法二: function f_name () { ...函數體... } 語法三: f_name (){ ...函數體... }
函數使用
-
函數的定義和使用:
-
可在交互式環境下定義函數
可將函數放在腳本文件中作為它的一部分
可放在只包含函數的單獨文件中
-
可在交互式環境下定義函數
-
調用:函數只有被調用才會執行
-
調用:給定函數名
函數名出現的地方,會被自動替換為函數代碼
-
調用:給定函數名
-
函數的生命周期:被調用時創建,返回時終止
-
函數返回值
-
函數的執行結果返回值:
(1) 使用echo或printf等命令進行輸出
(2) 函數體中調用命令的輸出結果 -
函數的退出狀態碼:
(1) 默認取決于函數中執行的最后一條命令的退出狀態碼
(2) 自定義退出狀態碼,其格式為:
return 從函數中返回,用最后狀態命令決定返回值
return 0 無錯誤返回。
return 1-255 有錯誤返回
-
函數的執行結果返回值:
交互式環境下定義和使用函數
-
示例:
$dir() { ls -l }
-
定義該函數后,若在$后面鍵入dir,其顯示結果同ls -l的作用相同
$dir -
該dir函數將一直保留到用戶從系統退出,或執行了如下所示的unset命令:
$ unset dir
在腳本中定義及使用函數
-
函數在使用前必須定義,因此應將函數定義放在腳本開始部分,直至shell首次發現它后才能使用
-
調用函數僅使用其函數名即可
示例:
$cat func1 #!/bin/bash # func1 hello() { echo "Hello there today's date is `date +%F`" } echo "now going to the function hello" hello echo "back from the function"
使用函數文件
-
可以將經常使用的函數存入函數文件,然后將函數文件載入shell
文件名可任意選取,但最好與相關任務有某種聯系。例如:functions.main -
一旦函數文件載入shell,就可以在命令行或腳本中調用函數。可以使用set命令查看所有定義的函數,其輸出列表包括已經載入shell的所有函數
-
若要改動函數,首先用unset命令從shell中刪除函數。改動完畢后,再重新載入此文件
-
創建函數文件
函數文件示例:
$cat functions.main #!/bin/bash #functions.main findit() { if [ $# -lt 1 ] ; then echo "Usage:findit file" return 1 fi find / -name $1 –print }
-
載入函數
函數文件已創建好后,要將它載入shell-
定位函數文件并載入shell的格式:
. filename 或 source filename
注意:此即<點> <空格> <文件名>;這里的文件名要帶正確路徑
示例:上例中的函數,可使用如下命令:
$ . functions.main -
定位函數文件并載入shell的格式:
-
檢查載入函數
使用set命令檢查函數是否已載入。set命令將在shell中顯示所有的載入函數示例:
$set findit=( ) { if [ $# -lt 1 ]; then echo "usage :findit file"; return 1 fi find / -name $1 -print }
-
執行shell函數
要執行函數,簡單地鍵入函數名即可示例:
$findit groups /usr/bin/groups /usr/local/backups/groups.bak
-
刪除shell函數
-
現在對函數做一些改動后,需要先刪除函數,使其對shell不可用。使用unset命令完成刪除函數
-
命令格式為:
unset function_name示例:$unset findit
-
命令格式為:
-
再鍵入set命令,函數將不再顯示
-
-
創建函數文件
環境函數
-
使子進程也可使用
聲明:export –f function_name
查看:export -f 或 declare -xf
函數參數
-
函數可以接受參數:
-
傳遞參數給函數:調用函數時,在函數名后面以空白分隔給定參數列表即可;例如“testfunc arg1 arg2 …”
-
在函數體中當中,可使用$1, $2, …引用這些參數;還可以使用$@或 $*引用所有參數, $#引用傳遞的參數的個數;
-
位置參數
script $1 此$1,為腳本位置參數
f_name $1 此$1,為函數位置參數
-
函數變量
-
變量作用域:
-
環境變量:當前shell和其子shell有效
-
本地變量:作用域是運行腳本的shell進程的生命周期;因此,其作用范圍為當前shell腳本程序文件,包括腳本中的函數;
- 只在當前shell進程有效,為執行腳本會啟動專用子shell進程;
-
局部變量:作用域是函數的生命周期;函數結束時變量被自動銷毀;
-
在函數中定義局部變量的方法
local NAME=VALUE -
如果函數中的變量未定義局部變量;則在函數執行之后對腳本的環境有效
注意:如果函數中有局部變量,如果其名稱同本地變量,會使用局部變量
-
在函數中定義局部變量的方法
-
函數遞歸
-
函數遞歸:
函數直接或間接調用自身
注意:遞歸層數 -
遞歸實例:
階乘是基斯頓 卡曼于 1808 年發明的運算符號,是數學術語一個正整數的階乘(factorial)是所有小于及等于該數的正整數的積,并且有0的階乘為1,自然數n的階乘寫作n!n!=1×2×3×…×n
-
階乘亦可以遞歸方式定義:0!=1,n!=(n-1)!×n
n!=n(n-1)(n-2)...1 n(n-1)! = n(n-1)(n-2)!
-
階乘亦可以遞歸方式定義:0!=1,n!=(n-1)!×n
-
階乘示例:
#!/bin/bash # fact() { if [ $1 -eq 0 -o $1 -eq 1 ]; then echo 1 else echo $[$1*$(fact $[$1-1])] fi } fact $1
-
fork炸彈
fork炸彈是一種惡意程序,它的內部是一個不斷在fork進程的無限循環,實質是一個簡單的遞歸程序。由于程序是遞歸的,如果沒有任何限制,這會導致這個簡單的程序迅速耗盡系統里面的所有資源-
函數實現
:(){ :|:& };: bomb() { bomb | bomb & }; bomb
-
腳本實現
cat Bomb.sh #!/bin/bash ./$0|./$0&
-
數組
數組
- 變量:存儲單個元素的內存空間
-
數組:存儲多個元素的連續的內存空間,相當于多個變量的集合
-
數組名和索引
- 數組名:整個數組只有一個名字;
-
索引:編號從0開始,屬于數值索引
注意:索引可支持使用自定義的格式,而不僅是數值格式,此即為關聯索引,bash4.0版本之后開始支持
- bash的數組支持稀疏格式(索引不連續)
-
數組名和索引
-
聲明數組:
declare -a ARRAY_NAME:聲明索引數組;
declare -A ARRAY_NAME:聲明關聯數組
注意:兩者不可相互轉換
數組賦值
-
索引數組
-
(1) 一次只賦值一個元素;
ARRAY_NAME[INDEX]=VALUE weekdays[0]="Sunday" weekdays[4]="Thursday"
-
(2) 一次賦值全部元素:
ARRAY_NAME=("VAL1" "VAL2" "VAL3" ...)
-
(3) 只賦值特定元素:支持稀疏格式的數組
ARRAY_NAME=([0]="VAL1" [3]="VAL2" ...)
-
(4) 交互式數組值對賦值
read -a ARRAY_NAME
-
-
關聯數組:注意:必須先聲明,再調用
declare -A ARRAY_NAME ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2‘...)
引用數組
-
引用數組元素:${ARRAY_NAME[INDEX]}
注意:引用時,只給數組名,省略[INDEX]表示引用下標為0的元素-
引用數組中的所有元素:
${ARRAY_NAME[*]}
${ARRAY_NAME[@]}
-
引用數組中的所有元素:
-
數組的長度(數組中元素的個數):
${#ARRAY_NAME} :表示數組中第一個元素中的字符串的長度; 數組中元素的個數: ${#ARRAY_NAME[*]} ${#ARRAY_NAME[@]}
-
示例:生成10個隨機數保存于數組中,并找出其最大值和最小值
#!/bin/bash declare -a rand declare -i max=0 declare –i min=32767 for i in {0..9}; do rand[$i]=$RANDOM echo ${rand[$i]} [ ${rand[$i]} -gt $max ] && max=${rand[$i]} [ ${rand[$i]} -lt $min ] && min=${rand[$i]} done echo "Max: $max Min:$min"
數組數據處理
-
引用數組中的所有元素:
-
${ARRAY_NAME[@]}
${ARRAY_NAME[*]} -
數組切片:${ARRAY[@]:offset:number}
- offset: 要跳過的元素個數
-
number: 要取出的元素個數;省略number時,表示取偏移量之后的所有元素;
${ARRAY[@]:offset}
-
${ARRAY_NAME[@]}
-
向非稀疏格式數組中追加元素:
ARRAY[${#ARRAY[*]}] -
刪除數組中的某元素:
unset ARRAY[INDEX] -
刪除整個數組:unset ARRAY
base的內置字符串處理工具
字符串切片:基于位置取子串
-
${#var}:返回字符串變量var的長度
-
${var:offset}:返回字符串變量var中從第offset個字符后(不包括第offset個字符)的字符開始,到最后的部分,offset的取值在0 到 ${#var}-1 之間(bash4.2后,允許為負值)
-
${var:offset:number}:返回字符串變量var中從第offset個
字符后(不包括第offset個字符)的字符開始,長度為
number的部分 -
${var: -length}:取字符串的最右側幾個字符
注意:冒號后必須有一空白字符 -
${var:offset:-length}:從最左側跳過offset字符,一直向右取到距離最右側lengh個字符之前的內容
-
${var: -length:-offset}:先從最右側向左取到length個字符開始,再向右取到距離最右側offset個字符之間的內容
基于模式取子串
-
${var#*word}:其中word可以是指定的任意字符;
功能:自左而右,查找var變量所存儲的字符串中,第一次出現的word, 刪除字符串開頭至第一次出現word字符之間的所有字符 -
${var##*word}:同上,貪婪模式,不同的是,刪除的是字符串開頭至最后一次由word指定的字符之間的所有內容
~]# file="var/log/messages“ ~]# echo ${file#*/} log/messages ~]# echo ${file##*/} messages
-
${var%word*}:其中word可以是指定的任意字符;
功能:自右而左,查找var變量所存儲的字符串中,第一次出現的word, 刪除字符串最后一個字符向左至第一次出現word字符之間的所有字符;~]# file="/var/log/messages" ~]# echo ${file%/*} /var/log
-
${var%%word*}:同上,只不過刪除字符串最右側的字符向
左至最后一次出現word字符之間的所有字符;url=http://www.magedu.com:80 ~]# echo ${url##*:} 80 ~]# echo ${url%%:*} http
查找替換
-
${var/pattern/substr}:查找var所表示的字符串中,第一次被pattern所匹配到的字符串,以substr替換之
-
${var//pattern/substr}: 查找var所表示的字符串中,所有能被pattern所匹配到的字符串,以substr替換之
-
${var/#pattern/substr}:查找var所表示的字符串中,行首被pattern所匹配到的字符串,以substr替換之;如果不是串首則不予替換;
-
${var/%pattern/substr}:查找var所表示的字符串中,行尾被pattern所匹配到的字符串,以substr替換之;如果不是串尾則不予替換;
注意:pattern中使用glob風格和通配符;
查找并刪除
-
${var/pattern}:查找var所表示的字符串中,刪除第一次被pattern所匹配到的字符串
-
${var//pattern}:查找var所表示的字符串中,刪除所有被pattern所匹配到的字符串
-
${var/#pattern}:查找var所表示的字符串中,刪除行首被pattern所匹配到的字符串
-
${var/%pattern}:查找var所表示的字符串中,刪除行尾被pattern所匹配到的字符串
-
字符大小寫轉換
-
${var^^}:把var中的所有小寫字母轉換為大寫
-
${var,,}:把var中的所有大寫字母轉換為小寫
-
高級變量
變量賦值
-
${var:-value}:如果var為空或未設置,那么返回value;否則返回var本身的值;可省略冒號;
-
${var:+value}:如果var不空,則返回value,否則返回空值;可省略冒號;
-
${var:=value}:如果var為空或未設置,那么返回value,并將value賦值給var;否則返回var的值
-
${var:?error_info}:如果var為空或未設置,那么在當前終端打印error_info;否則返回var的值
-
為腳本程序使用配置文件,實現變量賦值
-
(1) 定義文本文件,每行定義“name=value”
(2) 在腳本中source此文件即可
-
(1) 定義文本文件,每行定義“name=value”
高級變量用法-有類型變量
-
Shell變量一般是無類型的,但是bash Shell提供了declare和typeset兩個命令用于指定變量的類型,兩個命令是等價的
-
declare [選項] 變量名
-
-r 聲明或顯示只讀變量
-i 將變量定義為整型數
-a 將變量定義為數組
-A 將變量定義為關聯數組
-f 顯示此腳本前定義過的所有函數名及其內容
-F 僅顯示此腳本前定義過的所有函數名
-x 聲明或顯示環境變量和函數
-l 聲明變量為小寫字母 declare –l var=UPPER
-u 聲明變量為大寫字母 declare –u var=lower
-
-r 聲明或顯示只讀變量
-
eval命令
-
eval命令將會首先掃描命令行進行所有的置換,然后再執行該命令。該命令適用于那些一次掃描無法實現其功能的變量.該命令對變量進行兩次掃描
示例:
[root@server ~]# CMD=whoami [root@server ~]# echo $CMD whoami [root@server ~]# eval $CMD root [root@server ~]# n=10 [root@server ~]# echo {0..$n} {0..10} [root@server ~]# eval echo {0..$n} 0 1 2 3 4 5 6 7 8 9 10
-
eval命令將會首先掃描命令行進行所有的置換,然后再執行該命令。該命令適用于那些一次掃描無法實現其功能的變量.該命令對變量進行兩次掃描
間接變量引用
-
如果第一個變量的值是第二個變量的名字,從第一個變量引用第二個變量的值就稱為間接變量引用
-
variable1的值是variable2,而variable2又是變量名,variable2的值為value,間接變量引用是指通過variable1獲得變量值value的行為
variable1=variable2 variable2=value
-
bash Shell提供了兩種格式實現間接變量引用
-
eval tempvar=\$$variable1
-
tempvar=${!variable1}
示例:
[root@server ~]# N=NAME [root@server ~]# NAME=xxxxxx [root@server ~]# N1=${!N} [root@server ~]# echo $N1 xxxxxx [root@server ~]# eval N2=\$$N [root@server ~]# echo $N2 xxxxxx
-
創建臨時文件
-
mktemp命令:創建并顯示臨時文件,可避免沖突
-
mktemp [OPTION]… [TEMPLATE]
-
TEMPLATE:
filename.XXX;X至少要出現三個 -
OPTION:
-d: 創建臨時目錄
-p DIR或–tmpdir=DIR:指明臨時文件所存放目錄位置示例: # mktemp /tmp/test.XXX # tmpdir=`mktemp –d /tmp/testdir.XXX` # mktemp --tmpdir=/testdir test.XXXXXX
-
安裝復制文件
-
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
示例:
install -m 700 -o wang -g admins srcfile desfile
install –m –d /testdir/installdir -
選項:
信號捕捉
-
trap ‘觸發指令’ 信號
自定義進程收到系統發出的指定信號后,將執行觸發指令,而不會執行原操作 -
trap ” 信號
忽略信號的操作 -
trap ‘-‘ 信號
恢復原信號的操作 -
trap -p
列出自定義信號操作 -
列出信號:
trap -l
kill -l -
獲取信號詳細信息:man 7 signal
-
trap ‘COMMAND’ SIGNALS
??梢赃M行捕捉的信號:1)SIGHUP、2)SIGINT
#!/bin/bash # declare -a hosttmpfiles trap 'mytrap' INT mytrap(){ echo "Quit" rm -f ${hosttmpfiles[@]} exit 1 } for i in {1..254};do tmpfile=$(mktemp /root/ping.XXXXX) if ping -W 1 -c 1 172.16.$i.1 &> /dev/null;then echo "172.16.$i.1 is up"|tee $tmpfile else echo "172.16.$i.1 is down"|tee $tmpfile fi hosttmpfiles[${#hosttmpfiles[*]}]=$tmpfile done rm -f ${hosttmpfiles[@]}
#!/bin/bash trap 'echo “signal:SIGINT"' int trap -p for((i=0;i<=10;i++)) do sleep 1 echo $i done trap '' int trap -p for((i=11;i<=20;i++)) do sleep 1 echo $i done trap '-' int trap -p for((i=21;i<=30;i++)) do sleep 1 echo $i done
bash中使用ACSII顏色
\033[31m hello \033[0m ##m: 左側#: 3:前景顏色 4:背景顏色 右側#:顏色種類 1,2,3,4,5,6,7 #m: 加粗、閃爍等功能; 多種控制符,可組合使用,彼此之間用分號隔開; 可設置PS1,export設置;最好不要設置,有符號不容易消除;
dialog
-
可實現窗口化編程;
主要是如下:- 各窗體控件使用方式;
-
如何獲取用戶選擇或鍵入的內容?
- 默認,其輸出信息被定向到了錯誤輸出流;為使得其標準輸出,使用:–stdout
- dialog是默認安裝的,如使用必須自己安裝
原創文章,作者:s,如若轉載,請注明出處:http://www.www58058.com/76530