流程控制
? 過程式編程語言: 順序執行 選擇執行 循環執行
選擇執行:if語句 ?
注意:if語句可嵌套 ?
單分支
if 判斷條件:then
條件為真的分支代碼
fi ?
雙分支
if 判斷條件; then
條件為真的分支代碼
else
條件為假的分支代碼
fi
多分支
if CONDITION1; then
if-true
elif CONDITION2; then
if-ture
elif CONDITION3; then
if-ture
…
else all-false
fi ?
逐條件進行判斷,第一次遇為“真”條件時,執行其分支, 而后結束整個if語句
if實例
根據命令的退出狀態來執行命令
if ping -c1 -W2 station1 &> /dev/null; then
echo 'Station1 is UP'
elif grep "station1" ~/maintenance.txt &> /dev/null
then
echo 'Station1 is undergoing maintenance‘
else
echo 'Station1 is unexpectedly DOWN!'
exit 1
fi
選擇執行:case語句
1.語法
case $變量 in
PAT1) 分支1
;;
PAT2) 分支2
;;
…
*) 默認分支
;;
esac
2.case支持glob風格的通配符:
*: 任意長度任意字符
?: 任意單個字符
[]:指定范圍內的任意單個字符
a|b: a或b
練習
?1、寫一個腳本/root/bin/createuser.sh,實現如下功能: 使用一個用戶名做為參數,如果指定參數的用戶存在,就顯 示其存在,否則添加用戶;顯示添加的用戶的id號等信息
#!/bin/bash
useradd $1 &> /dev/null
if [ $? -eq 0 ]
then
echo 'user $1 added successful'
id $1
else
echo 'user exsit'
fi
?
2、寫一個腳本/root/bin/yesorno.sh,提示用戶輸入yes或 no,并判斷用戶輸入的是yes還是no,或是其它信息
#!/bin/bash
read -p "please input yes or no! " a
case $a in
[Yy][Ee][Ss]|[Yy])
echo 'yes'
;;
[Nn][Oo]|[Nn])
echo 'no'
;;
*)
echo 'others'
esac?
3、寫一個腳本/root/bin/filetype.sh,判斷用戶輸入文件路 徑,顯示其文件類型(普通,目錄,鏈接,其它文件類型)
#!/bin/bash
read -p "please enter file: " filename
if [ -L $filename ];then
echo "$filename is Symbol file."
elif [ -c $filename ];then
echo "$filename is char file."
elif [ -d $filename ];then
echo "$filename is dir file."
elif [ -b $filename ];then
echo "$filename is block file."
elif [ -f $filename ];then
echo "$filename is common file."
elif [ -P $filename ];then
echo "$filename is pipe file."
elif [ -S $filename ];then
echo "$filename is socket file."
else
echo "please enter a Correct file"
fi?4、寫一個腳本/root/bin/checkint.sh,判斷用戶輸入的參數 是否為正整數
read -p 'please input numbers' n
[ -z "$n" ] && echo 'you must input numbers' && exit
[ "$n" -eq 0 ] &> /dev/null && echo "$n is 零" && exit
if let var=$n &> /dev/null;then
if [ "$?" -eq 0 ];then
if [ "$n" -lt 0 ];then
echo "$n is 負整數"
else
echo "$n is 正整數"
fi
fi
else
echo "$n 不是一個整數"
fi
循環執行
將某代碼段重復運行多次
重復運行多少次: 循環次數事先已知 ,循環次數事先未知 。
進入條件:條件滿足時才進入循環
退出條件:每個循環都應該有退出條件
for, while, until
for循環 ?
兩種方式:(1)遍歷列表
for 變量名 in 列表;do
循環體
done ?
進入條件:只要列表有元素,即可進入循環
退出條件:列表中的元素遍歷完成
(2)控制變量
執行機制:依次將列表中的元素賦值給“變量名”; 每次賦值后即執 行一次循環體; 直到列表中的元素耗盡,循環結束。
列表生成方式:(1) 直接給出列表
(2) 整數列表 (a) {start..end}
(b) $(seq [start [step]] end)
(3) 返回列表的命令 $(COMMAND)
(4) 使用glob,如:*.sh
(5) 變量引用; $@, $*
練習:用for實現 ?
-
1. 判斷/var/目錄下所有文件的類型
for filename in /var/log/*;do
if [ -L $filename ];then
echo "$filename is Symbol file."
elif [ -c $filename ];then
echo "$filename is char file."
elif [ -d $filename ];then
echo "$filename is dir file."
elif [ -b $filename ];then
echo "$filename is block file."
elif [ -f $filename ];then
echo "$filename is common file."
elif [ -P $filename ];then
echo "$filename is pipe file."
elif [ -S $filename ];then
echo "$filename is socket file."
else
echo "please enter a Correct file"
fi
done?
2.添加10個用戶user1-user10,密碼同用戶名
if [[ $# -lt 1 ]]
then
echo -e "Error: No option \n\t-d\tdelete user1-user10\n\t-a\tadd user1-user10 "
exit 1
fi
for i in $(seq 10);do
case $1 in
-d|–del)
if id user$i &> /dev/null;then
userdel -r user$i
echo "user$i: Delete complete!"
else
echo "user$i: No such user!"
fi
;;
-a|–add)
if id user$i &> /dev/null;then
echo "user$i"| passwd –stdin "user$i" &> /dev/null
echo -e "user$i: Already existed!\nAnd authentication tokens updated successful!"
else
useradd user$i &> /dev/null
echo "user$i"| passwd –stdin "user$i" &> /dev/null
echo " user$i: Add complete!"
fi
;;
*)?
3./etc/rc.d/rc3.d目錄下分別有多個以K開頭和以S開頭的 文件;分別讀取每個文件,以K開頭的文件輸出為文件加stop ,以S開頭的文件輸出為文件名加start;
“K34filename stop”
“S66filename start”
for i in `ls /etc/rc.d/rc3.d`
do
FileC1=`echo $i | cut -c1`
case $FileC1 in
K)
echo -e "$i\tstop"
;;
S)
echo -e "$i\tstart"
;;
*)
echo "unkown file"
;;
esac
done
4.寫一個腳本,提示輸入正整數n的值,計算1+2+3+…n的 總和
#!/bin/bash
read -p 'please input the number' n
num=`echo $n|grep "^[[:digit:]]\+$"`
if echo $n|grep -q "^[[:digit:]]\+$" ;then
if [ $num -eq 0 ];then
echo "the number is 0"
exit
fi
else
echo "the number is negtive"
exit
fi
string=0
for i in `seq $n`;do
sum=$[$sum+$i]
string=$string+$i
done
echo "$string=$sum"
5.寫一個腳本,提示請輸入網絡地址,如192.168.0.0,判 斷輸入的網段中主機在線狀態 ?
echo -n "Enter IP:"
read IP
#get IP segment
Segment=`echo $IP | cut -d. -f1-3 `.
#
if echo $IP | egrep '\<([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){2}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\>' &>/dev/null
then
#test only 5 host
for i in `seq 240 255`
do
if ping -c 1 -W 1 $Segment$i &> /dev/null
then
echo -e "$Segment$i\tonline"
else
echo -e "$Segment$i\toffline"
fi
done
else
echo "$IP is invalid"
fi
6.打印九九乘法表
#!/bin/bash
for j in {1..9};do
for i in $(seq 1 $j);do
echo -n -e "${i}X${j}=$[${i}*${j}]\t"
done
echo
done
while循環 ?
while CONDITION; do
循環體
done ?
進入條件:CONDITION測試為真
退出條件:CONDITION測試為假
CONDITION:循環控制條件;進入循環之前,先做一次判 斷;每一次循環之后會再次做判斷;條件為“true”,則執行 一次循環;直到條件測試狀態為“false”終止循環,因此:CONDTION一般應該有循環控制變量;而此變量的值 會在循環體不斷地被修正 .?
進入條件:CONDITION為true; ?
退出條件:CONDITION為false
練習:用while實現 ?
1.求100以內所有正整數之和 ?
2.通過ping命令探測172.16.250.1-254范圍內的所有主機 的在線狀態,統計在線主機和離線主機各多少。
3.打印九九乘法表
4.利用變量RANDOM生成10個隨機數字,輸出這個10數字 ,并顯示其中的最大者和最小者
5.打印國際象棋棋盤
until循環 ?
until CONDITION; do
循環體 ?
Done
進入條件:CONDITION測試為假
退出條件:CONDITION測試為真
練習:用until實現
-
1. 每隔3秒鐘到系統上獲取已經登錄的用戶的信息;如果發 現用戶hacker登錄,則將登錄時間和主機記錄于日志 /var/log/login.log中,并提示該用戶退出系統。
#!/bin/bash
while true ;do
if who | grep "^hacker\>" &> /dev/null;then
break
fi
sleep 3
done
echo "$(date +"%F %T") hacker logged on" >> /tmp/users.log
#!/bin/bash
until who | grep "^hacker\>" &> /dev/null;do
sleep 3
done
echo "$(date +"%F %T) hacker logged on" >> /tmp/users.log
2.隨機生成10以內的數字,實現猜字游戲,提示比較大或小 ,相等則退出。
break循環控制語句
? 用于循環體中 ?
break [N]:提前結束第N層循環,最內層為第1層
while CONDTIITON1; do
CMD1
…
if CONDITION2; then
break
fi
CMDn
…
Done
整個until循環結束
exit是退出整個腳本
退出內循環,執行外循環
死循環創建
while true; do
循環體 ?
Done
退出方式:某個測試條件滿足時,讓循環體制性break命令
until false; do
循環體 ?
Done
for特殊用法
1.while循環的特殊用法(遍歷文件的每一行):
while read line; do
循環體
done < /PATH/FROM/SOMEFILE ?
依次讀取/PATH/FROM/SOMEFILE文件中的每一行,且將 行賦值給變量line
練習
掃描/etc/passwd文件每一行,如發現GECOS字段為空,則填 充用戶名和單位電話為62985600,并提示該用戶的GECOS信 息修改成功。
2.雙小括號方法
即((…))格式,也可以用于算術運算 ?
雙小括號方法也可以使bash Shell實現C語言風格的變量操作
#I=10
#((I++))
for循環的特殊格式:
for ((控制變量初始化;條件判斷表達式;控制變量的修正表達式))
do
循環體
done ?
控制變量初始化:僅在運行到循環代碼段時執行一次。 ?
控制變量的修正表達式:每輪循環結束會先進行控制變量修正運算 ,而后再做條件判斷。
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中變量進行修改
定義函數
? 函數由兩部分組成:函數名和函數體。 ?
語法一: function f_name {
…函數體…
} ?
語法二: function f_name () {
…函數體…
} ?
語法三: f_name () {
…函數體…
}
如圖,函數中不能直接使用$1等數字參數,所以用過度的中間參數。
函數使用
函數的定義和使用: 可在交互式環境下定義函數 (測試環境下使用)
可將函數放在腳本文件中作為它的一部分
可放在只包含函數的單獨文件中 ?
調用:函數只有被調用才會執行;
調用:給定函數名
函數名出現的地方,會被自動替換為函數代碼 ?
函數的生命周期:每次被調用時創建,返回時終止
函數返回值
函數有兩種返回值: ?
1.函數的執行結果返回值: (1) 使用echo或printf命令進行輸出
(2) 函數體中調用命令的輸出結果 ?
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
檢查載入函數
? 使用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命令,函數將不再顯示
函數參數
? 函數可以接受參數: 傳遞參數給函數:調用函數時,在函數名后面以空白分隔 給定參數列表即可;例如“testfunc arg1 arg2 …” 在函數體中當中,可使用$1, $2, …調用這些參數;還 可以使用$@, $*, $#等特殊變量。
示例1:添加10個用戶
添加用戶的功能使用函數實現,用戶名作為參數傳遞給函數
#!/bin/bash
#2:user exist
#函數定義
addusers() {
#判斷用戶是否存在
if id $1 &> /dev/null; then
#存在就返回一個2
return 2
else
#不存在就添加用戶
useradd $1
#用戶是否添加成功
retuval=$?
#返回添加成功的值
return $retuval
fi
}
#引用函數
for i in {1..10};do
addusers ${1}${i}
#位置參數不能直接被引用
retuval=$?
#調用addusers的參數,如果$?=0則表示用戶添加成功
if [ $retuval -eq 0 ];then
#${1}是用戶名,${i}是用戶后面跟著的數字如user1,user是給定的用戶名
echo "add user ${1}${i} finished"
elif [ $retuval -eq 2 ];then
echo "user ${1}${i} exists"
else
echo "unknow error"
fi
done
函數變量
?變量作用域:
環境變量:當前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)!
? 示例: fact.sh
#!/bin/bash
#
fact() {
if [ $1 -eq 0 -o $1 -eq 1 ]; then
echo 1
else
echo $[$1*$(fact $[$1-1])]
fi
}
fact $1
原創文章,作者:Jagger,如若轉載,請注明出處:http://www.www58058.com/40001
路過,腳本習題很好,點個贊!