小阿軒yx-Shell 編程之循環語句與函數
for 循環語句
可以很好地解決順序編寫異常煩瑣、困難重重的全部代碼
(){}:里邊寫的都是命令
):不能嵌套
$():可以嵌套,適合更復雜的命令
命令執行的兩種方式
- 交互式:在命令行里直接寫入命令,有多少需求就寫入多少次命令
- 非交互式:把命令寫入到腳本里去執行,人就不用在腳本里敲命令
(注:交互:人與機器之間的一種交流)
# 不想看到消息提示就放到(&>/dev/null黑洞)里面
echo "123456" | passwd --stdin $uname &>/dev/null
?echo:作用回顯(將內容顯示到屏幕)
for 語句的結構
(注:for循環語句需要有一個取值列表)
for 變量名 in 取值列表
do
命令序列
done
?根據姓名列表批量添加用戶
# 創建用戶的列表文件
[root@localhost ~]# vim /root/users.txt
zhangsan
lisi
wangwu
# 編輯批量添加用戶的腳本
[root@localhost ~]# vim uaddfor.sh
#!/bin/bash
ULIST=$(cat /root/users.txt)
for UNAME in $ULIST
douseradd $UNAMEecho "123456" | passwd --stdin $UNAME &>/dev/null
done
給腳本賦予執行權限
[root@localhost ~]# chmod +x uaddfor.sh
# 測試并確認執行結果
[root@localhost ~]# ./uaddfor.sh
[root@localhost ~]# cat /etc/passwd
?編輯批量刪除用戶的腳本
[root@localhost ~]# vim udelfor.sh
#!/bin/bash
ULIST=$(cat /root/users.txt)
for UNAME in $ULIST
do# 不想看到消息提示就放到黑洞里userdel -r $UNAME &>/dev/nulldone
# 給腳本賦予執行權限
[root@localhost ~]# chmod a+x udelfor.sh
# 測試并確認執行結果
[root@localhost ~]# ./udelfor.sh
[root@localhost ~]# cat /etc/pwd
根據 IP 地址列表檢查主機狀態
# 創建IP地址列表文件
[root@localhost ~]# vim /root/ipadds.txt
172.16.1.1
172.16.1.111
172.16.1.222
192.168.10.10
# 編輯循環檢查各主機的腳本
[root@localhost ~]# vim chkhosts.sh
#!/bin/bash
HLIST=$(cat /root/ipadds.txt)
for IP in $HLIST
doping -c 3 -i 0.2 -W 3 $IP &> /dev/nullif [ $? -eq 0 ]thenecho "Host $IP is up."elseecho "Host $IP is down."fi
done
# 添加執行權限
[root@localhost ~]# chmod a+x chkhost.sh
# 測試并確認執行結果
[root@localhost ~]# bash chkhosts.sh
- 需要指定一個變量及可能的取值列表
????????取值列表:稱為 for 語句的執行條件,其中包括多個屬性相同的對象,需要預先指定
- 針對每個不同的取值重復執行相同的命令序列
- 直到變量值用完退出循環從而不會進入死循環
使用 while 循環語句
可用于
- 求控制循環次數
- 操作對象按數字順序編號
- 按特定條件執行重復操作
while 語句的結構
while 條件測試操作
do
命令序列
done
# 批量添加用戶腳本
[root@localhost ~]# vim uaddwhile.sh
#!/bin/bash
PREFIX="stu"
i=1
while [ $i -le 20 ]
douseradd ${PREFIX}$iecho "123456" | passwd --stdin ${PREFIX}$i &> /dev/nulllet i++
done
# 添加執行權限
[root@localhost ~]# chmod a+x uaddwhile.sh
# 測試并確認執行結果
[root@localhost ~]# bash uaddwhile.sh
[root@localhost ~]# grep "stu" /etc/passwd | tail -3或者#!/bin/bash
PREFIX="stu"
i=1
while [ $i -le 20 ]
douseradd ${PREFIX}$iecho "123456" | passwd --stdin ${PREFIX}$i &> /dev/nulli=`expr $i + 1`
done
# 批量刪除用戶腳本
[root@localhost ~]# vim udelwhile.sh
#!/bin/bash
PREFIX="stu"
i=1
while [ $i -le 20 ]
douserdel -r ${PREFIX}$ilet i++done
# 添加執行權限
[root@localhost ~]# chmod a+x udelwhile.sh
# 測試并確認執行結果
[root@localhost ~]# bash udelwhile.sh
id stu20
猜價格?
[root@localhost ~]# vim pricegame.sh
#!/bin/bash
PRICE=$(expr $RANDOM % 1000)
TIMES=0
echo "商品實際價格范圍為0-999,猜猜看是多少?"
while true
doread -p "請輸入你猜測的價格數目:" INTlet TIMES++if [ $INT -eq $PRICE ] ; thenecho "恭喜你答對了,實際價格是 $PRICE"echo "你總共猜測了$TIMES 次"exit 0elif [ $INT -gt $PRICE ] ; thenecho "太高了!"elseecho "太低了!"fi
done
(?注:linux中隨機數的取值范圍是0--32767,和什么數取余,取余后的最大數就是誰,不包含該數字)
# 添加執行權限
[root@localhost ~]# chmod a+x pricegame.sh
# 測試并確認執行結果
[root@localhost ~]# bash pricegame.sh
- 不需要取值列表
- 可以根據特定的條件反復執行一個命令序列,直到該條件不再滿足時為止。
- 在腳本應用中,要避免出現死循環的情況,否則后邊的命令操作將無法執行。
- 因此,循環體內的命令序列中應包括修改測試條件的語句,以便在適當的時候使測試條件不再成立,從而結束循環
while 循環語句的兩個結果(只有兩個)
- true(真)
- false(假)
用 true 作為條件時
- 表示條件永遠成立,循環體內的命令序列將無限執行下去,除非強制終止腳本(或通過 exit 語句退出腳本)
反之用 false 作為條件
- 則循環體將不會被執行
(注:這兩個特殊條件也可以用在 if 語句的條件測試中)
until 循環語句
直到性循環
(注:一般處理一些有特色的)
until 循環語句的結構
until 條件測試操作
do
done
案例一
[root@localhost ~]# vim until_v1.sh
#!/bin/bash
i=0;s=0
until [ $i -eq 50 ]
do
let "i=$i+1";let "s=$s+$i"
done
echo 'sum(1..50)='$s# 添加執行權限
[root@localhost ~]# chmod +x sum1to50_until_v1.sh# 測試結果
[root@localhost ~]# bash sum1to50_until_v1.sh
?案例二
[root@localhost ~]# vim until_v2.sh
var1=100
until [ $var1 -eq 0 ]
doecho $var1var1=$[ $var1 - 25 ]
done# 添加執行權限
[root@localhost ~]# chmod +x until_v2.sh# 測試結果
[root@localhost ~]# bash until_v2.sh
- 與 while 循環類似
- while 循環能實現的腳本 until 同樣也可以實現
區別
- while 循環在條件為真時繼續執行循環
- 而 until 則是在條件為假時執行循環
Shell 函數
代碼的重用
local:作用將變量設置為局部變量
函數的用法
案例一
[function] 函數名() {
[return x]
}
[root@localhost ~]# cat exa1.sh
#!/bin/bash
zhangsan() {echo "my name is zhangsan"
}lisi() {echo "my name is lisi"
}zhangsan
lisi
# 測試結果
[root@localhost ~]# bash exa1.sh
案例二?
[root@localhost ~]# cat exa2.sh
#!/bin/bash
name() {echo "my name is $1"
}
name $1
# 測試結果
[root@localhost ~]# bash exa2.sh zhangsan
my name is zhangsan
[root@localhost ~]# bash exa2.sh lisi
my name is lisi
案例四?
[root@localhost ~]# vim fun_scope.sh
myfun ()
{ local i i=8 echo $i
}
i=9
myfun
echo $i
# 添加執行權限
[root@localhost ~]# chmod +x fun_scope.sh
# 測試結果
[root@localhost ~]# bash fun_scope.sh
(注:通過內置命令 local 將變量的值限定在函數內部)
Shell 腳本執行的過程中
- 函數被置于內存中
- 每次調用函數時不需要從硬盤讀取,因此運行的速度比較快
Shell 編程中函數并非是必須的元素
使用函數的好處
- 可以對程序進行更好的組織
- 將一些相對獨立的代碼變成函數
- 可以提高程序可讀性與重用性
- 避免編寫大量重復代碼
- “function”關鍵字表示定義一個函數,可以省略;
- “{”符號表示函數執行命令的入口,該符號可以與函數名同行也可以在函數名下一行的句首;
- “}”符號表示函數體結束,兩個大括號之間{ }是函數體;
- “命令序列”部分可以是任意的 Shell 命令,也可以調用其他函數;
- “return”表示退出函數返回一個退出值,通過返回值判斷執行是否成功,也可以使用 exit 終止整個 Shell 腳本
Shell 函數調用的方法為
函數名 [參數 1] [參數 2]
函數變量的作用范圍
- Shell 腳本中函數的執行不會開啟一個新的子 Shell,而是僅在當前定義的 Shell環境中有效
- 如果 Shell 腳本中的變量沒有經過特殊設定,默認在整個腳本中都是有效的
- 編寫腳本時,有時需要將變量的值限定在函數內部,可以通過內置命令 local 來實現
- 函數內部變量的使用,可以避免函數內外同時出現同名變量對腳本結果的影響
函數的參數
- 在使用函數參數時,函數名稱在前參數在后,函數名和參數之間用空格分隔,可以有多個參數,參數使用$1、$2、$3……的方式表示
- 以此類推,從第 10 個參數開始,調用方法為${10},不加大括號無法調用成功
遞歸函數
遞歸算法的經典例子是計算階乘
案例
[root@localhost ~]# vim fun_recursion.sh
#!/bin/bash
function factorial { if [ $1 -eq 1 ] then echo 1 else local temp=$[ $1 - 1 ] local result=$(factorial $temp) echo $[ $result * $1 ] fi
} read -p "Enter value: " value
result=$(factorial $value)
echo "The factorial of $value is: $result"
# 測試結果
[root@localhost ~]# bash fun_recursion.sh
調用自己本身的函數實現遞歸函數
Linux 系統上編寫 Shell腳本的時候,經常需要遞歸遍歷系統的目錄,列出目錄下的文件和目錄,逐層遞歸列出,并對這些層級關系進行展示
Shell 數組
Shell 腳本中,數組是一種常見的數據結構
主要的應用場景包括
數組常用定義方法包括以下幾種
方法一
數組名=(value0 value1 value2 ...)
方法二
數組名=([0]=value [1]=value [2]=value ...)
方法三
列表名=”value0 value1 value2 ...”
數組名=($列表名)
方法四
數組名[0]=”value”
數組名[1]=”value”
數組名[2]=”value”
......
獲取數組的長度?
[root@localhost ~]# arr_number=(1 2 3 4 5)
[root@localhost ~]# arr_length=${#arr_number[*]}
[root@localhost ~]# echo $arr_length
5
[root@localhost ~]# arr_length_1=${#arr_number[@]}
[root@localhost ~]# echo $arr_length_1
5
讀取某下標賦值
[root@localhost ~]# arr_index2=${arr_number[2]}
或
[root@localhost ~]# echo ${arr_number[2]}
//第三個元素
[root@localhost ~]# echo $arr_index2
3或
echo ${aaa[3]}
數組遍歷?
[root@localhost ~]# vim array_traverse.sh
#!/bin/bash
arr_number=(1 2 3 4 5)
for v in ${arr_number[@]}
do
echo $v
done
# 添加執行權限
[root@localhost ~]# chmod +x array_traverse.sh
# 測試結果
[root@localhost ~]# bash array_traverse.sh
數組切片?
[root@centos-7 ~]# arr=(1 2 3 4 5)
//輸出整個數組
[root@centos-7 ~]# echo ${arr[@]}
1 2 3 4 5
//${數組名[@或*]:起始位置:長度}
[root@centos-7 ~]# echo ${arr[@]:0:2}
1 2
[root@centos-7 ~]# echo ${arr[@]:2:3}
3 4 5
數組替換?
[root@centos-7 ~]# arr=(1 2 3 4 5)
// ${數組名[@或*]/查找字符/替換字符}
[root@centos-7 ~]# echo ${arr[@]/4/66}
1 2 3 66 5
// 并不會替換數組原有內容
[root@centos-7 ~]# echo ${arr[@]}
1 2 3 4 5
// 要實現改變原有數組,可通過重新賦值實現
[root@centos-7 ~]# arr=(${arr[@]/4/66})
[root@centos-7 ~]# echo ${arr[@]}
1 2 3 66 5
數組刪除?
[root@centos-7 ~]# arr=(1 2 3 4 5)
[root@centos-7 ~]# unset arr
// 刪除數組
[root@centos-7 ~]# echo ${arr[*]}
[root@centos-7 ~]# arr=(1 2 3 4 5)
[root@centos-7 ~]# unset arr[2]
// 刪除第三個元素
[root@centos-7 ~]# echo ${arr[*]}
1 2 4 5
(注:$* 和 $@ 都表示傳遞給函數或腳本的所有參數)
區別
- $* 和 $@ 不被雙引號" "包圍時,它們之間沒有任何區別,都是將接收到的每個參數看做一份數據,彼此之間以空格來分隔
被雙引號" "包含時,就會有區別
"? " 會 將 所 有 的 參 數 從 整 體 上 看 做 一 份 數 據 , 而 不 是 把 每 個 參 數 都 看 做 一 份 數 據?
"@"仍然將每個參數都看作一份數據,彼此之間是獨立的
Shell 腳本調試
[root@localhost ~]# vim aaa.sh #!/bin/bash
set -x
##開啟調試模式 read -p "請輸入您的分數(0-100):" GRADE
if [ $GRADE -ge 85 ] && [ $GRADE -le 100 ] then echo "$GRADE 分!優秀"
set +x
##關閉調試模式 elif [ $GRADE -ge 70 ] && [ $GRADE -le 84 ] thenecho "$GRADE 分,合格"
else echo "$GRADE 分?不合格"
fi
- 把復雜的腳本簡單化
- 要思路清晰
- 分段實現
(注:執行腳本時出現錯誤后,不要只看提示的錯誤行,要觀察整個相關的代碼段)
為避免編寫的腳本出錯,除了在編寫腳本時注意書寫規范,排除語法錯誤,更重要的是利用調試腳本工具來調試腳本。
echo 命令是最有用的調試腳本工具之一,一般在可能出現問題的腳本中加入 echo 命令,采用的是分段排查的方式
(注:除了 echo 命令之外,bash Shell 也有相應參數可以調試腳本)
常用參數的具體含義為
- -n:不會執行該腳本,僅查詢腳本語法是否有問題,如果沒有語法問題就不顯示任何內容,如果有問題會提示報錯
- -v:在執行腳本時,先將腳本的內容輸出到屏幕上然后執行腳本,如果有錯誤,也會給出錯誤提示
- -x:將執行的腳本內容輸出到屏幕上,這個是對調試很有用的參數
?小阿軒yx-Shell 編程之循環語句與函數