bash腳本編程之判斷和循環
變量:
特性:一個變量中只能存儲一個數值;
數組:能夠容納多個數組元素的連續的內存空間;
1)稀疏數組:
2)稠密數組:
數組元素:數組中任何一個存放數據的存儲單元,其作用相當於一個變量;
數組元素的標識:
索引數組標識:所有的數組元素都是使用數字編號的;
通常數字標號是從0開始的,即:0,1,2....
關聯數組標識:所有的數組元素都可以使用名稱(字符串)來標識;
注意:bash4.0以上版本纔有可能支持關聯數組;
數組聲明定義:
1.declare命令:
-a to make NAMEs indexed arrays (if supported)
將其後的變量名稱聲明爲索引數組;declare -a names=([0]='z'[1]='w' [2]='1')
-A to make NAMEs associative arrays (if supported)
將其後的變量名稱聲明爲關聯數組;declare -a names=( "z" "w" "l" )
2.直接使用變量賦值的方式:
定義稠密的索引數組
ARRAY-NAME=(“VALUE1” “VALUE2”....)
定義稀疏數組:
ARRAY-NAME=([0]=“VALUE1” [1]=“VALUE2”....)
定義關聯數組:
ARRAY-NAME=(INDEX_name1='VALUE1' INDEX_VALUE2='VALUE2'....)
3.分別定義數組元素:
ARRAY_NAME[0]='VALUE'
ARRAY_NAME[1]='VALUE'
ARRAY_NAME[2]='VALUE'
.......
引用數組元素的方式:
${ARRAY_NAME[INDEX]}
注意:在引用數組元素時,沒人給出索引號,默認編號爲”0“,即顯示第一個數組元素的數值;
引用整個數組中所有元素:
${ARRAY_NAME[*]}或 ${ARRAY_NAME[@]}
引用整個數組的所有元素的索引號:
${!ARRAY_NAME[*]}或 ${!ARRAY_NAME[@]}
查看數組中的元素個數(數組長度):
${#ARRAY_NAME[*]}或 ${#ARRAY_NAME[@]}
數組切片:
${ARRAY_NAME[*]:offset}
//顯示包括offste數值所對錶示的位置的元素極其後所有的元素;
${ARRAY_NAME[*]:offset:number}
//顯示數組中包括offset數值所對應表示的位置的元素及其後number個元素;
數組撤銷:
unset ARRAY_NAME
RANDOM變量:
隨機數變量:0-32767,整數值;
從熵池中取隨機數;
熵池:
/dev/random
兩次敲擊鍵盤的時間間隔;
兩次IO的時間間隔;
...
/dev/urandom:僞熵池
利用應用程序計算得到的隨機數;
[root@localhost ~]# echo $RANDOM
25570
[root@localhost ~]# echo $RANDOM
8882
[root@localhost ~]# echo $RANDOM
2373
[root@localhost ~]# echo $RANDOM
4870
[root@localhost ~]# echo $RANDOM
3895
[root@localhost ~]# echo $RANDOM
8353
[root@localhost ~]# echo $RANDOM
14099
bash腳本編程的結構:
bash腳本編程語言:
腳本類語言
解釋型語言
過程式編程語言
過程式編程語言結構:
順序執行結構:默認
從上而下,自左而右地執行所有的語句(命令);
選擇執行結構:
當條件滿足或不足時,纔會執行對應的語句(命令);
循環執行結構:
重複執行某段語句(命令);
bash腳本編程語中也具有上述結構;
順序執行結構:默認
選擇執行結構:
根據給定條件邏輯判斷結果或根據某個可選取的取值範圍,進而選擇某個分支結構中的命令語句予以執行的方式;
if:
選擇執行結構的標準,根據條件的邏輯判斷結果選擇執行的語句內容;
case:
選擇執行結構的標準,根據符合某特定範圍的取值標準選擇執行的語句內容;
循環執行結構:
對於特定語句內容 ,重複執行0,1,多次;
for:以遍歷列表的方式進行循環;
while:根據給定條件的邏輯判斷結果進行循環,邏輯判斷結果爲真,循環,否則,停止循環;
until:根據給定條件的邏輯判斷結果進行循環,邏輯判斷結果爲假,循環,否則,停止循環;
select:死循環,即沒有默認退出條件的循環;利用循環提供一個可選擇的列表;
bash腳本的執行結構之if選擇執行結構:
if語句:if - if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMMANDS; ] fi
if語句的單分支結構:
if - if COMMANDS; then COMMANDS; fi
注意:是否會執行then後面的命令,取決於if後面的命令的執行狀態返回值:
1.如果其返回值爲真,則執行then後面的命令;
2.如果其返回值爲假,則不執行then後面的命令;
建議在腳本中的書寫格式:
if CONDITION ; then
statement
...
fi
或
if CONDITION
then
STATEMENT
....
fi
if語句的雙分支結構:
if - if COMMANDS; then COMMANDS; else COMMANDS; fi
注意:是否執行then後的命令或else後的命令,取決於if後的命令執行狀態的返回值;
1.如果返回值爲真,則執行then後的命令;
2.如果爲假,則執行else後的命令;
if語句的多分支結構:
if - if COMMANDS; then COMMANDS; else COMMANDS; fi
注意:是否執行then後的命令或else後的命令,取決於if後的命令執行狀態的返回值或else後的命令執行狀態返回值;
1.首先判斷if後的命令的狀態返回值是否爲真,爲真就執行then後的語句;如果爲假,就繼續判斷第一個elif後的命令
執行狀態返回值;
2.第一個elif後的命令執行狀態返回值爲真,就執行第一個elif語句中then後的命令,否則,就繼續判斷第二個elif後的
執行狀態返回值;
3.以此類推,會判斷每個elif後面的命令的執行狀態返回值是否爲真;如果所有的if和elif後面的命令的執行狀態返回值均爲假,則執行else後面的語句;
建議在腳本中的書寫格式:
if CONDITION1 ; then
STATEMENT
...
elif CONDITION2 ; then
STATEMENT
...
elif CONDITION3 ; then
STATEMENT
...
...
else
STATEMENT
...
fi
或
if CONDITION
then
STATEMENT
...
elif CONDITION2 ; then
STATEMENT
...
elif CONDITION3 ; then
STATEMENT
...
...
else
STATEMENT
...
fi
注意:if的多分支結構,使用場景不多,而且有時候,可以使用嵌套的多分支或雙分支if結構代替if多分支結構;
嵌套的if結構
if CONDITION1 ; then
if CONDITION2 ; then
if CONDITION3 ; then
STATEMENT
...
else
STATEMENT
...
else
STATEMENT
...
fi
else
STATEMENT
...
fi
COMMAND1 && COMMAND2 || COMMAND3
示例:
寫腳本,判斷某個用戶的默認登錄shell是否爲/bin/bash
#!/bin/bash
y=$(cut -d: -f 1 /etc/shadow | sort -R | head -1)
w=$(egrep "^$y\>" /etc/passwd | cut -d: -f 7)
if [ "$w"=="/bin/bash" ] ; then
echo "${y}'s login shell is /bin/bash."
else
echo "${y}'s login shell is ${w}"
fi
unset y w
bash腳本編程之用戶交互使用:
位置參數變量:
$0:命令的本身,對於腳本而言,就是該腳本的路徑;
$1,$2,....$N:腳本後面通過命令行給腳本傳遞的命令行參數;
N>9時,引用該位置變量時需要加{},即${10}
特殊變量:
$@:給出的所有位置的參數的列表,當使用雙引號時,每個參數作爲單獨的字符串存在;
$*:給出的所有位置的參數的列表,當使用雙引號時,每個參數作爲單獨的字符串存在;
$#:表示除去$0之外,整個命令行中有多少個參數;
read命令:
read [-ers] [-a 數組] [-d 分隔符] [-i 緩衝區文字] [-n 讀取字符數]
[-N 讀取字符數] [-p 提示符] [-t 超時] [-u 文件描述符] [名稱 ...]
-a array:定義數組(索引數組);
-p prompt:給用戶輸出提示信息;
-t timeout:用戶輸入的超時時間;
name :變量或數組的名稱;如果省略此內容,bash會將read讀到的信息直接保存到內置的名爲REPLAY變量中;
注意:
Linux思想之一:儘量不與用戶交互;
在使用read命令時,通常會使用-t選項來指定與用戶的交互時間,一旦超過預定時間,腳本中後續的命令內容
會自動被執行;因此,通常需要在後面判斷通過read賦值的變量值是否爲空,如果爲空,可能爲該變量提供默認值;
read -t 5 VAR1
[ -z $VAR1 ] && VAR1=value1
管理用戶腳本:
腳本可以接受兩個參數,第一個參數爲-a或-d,第二個參數爲用戶名;如果第一個參數是-a,則創建其後面參數命名的用戶;如果第一個參數爲-d,則刪除其後面參 數命名的用戶;
#!/bin/bash
#
if [ $# -ne 2 ] ; then
echo "Make sure provide TWO arguments."
exit 5
fi
if [ $1 == '-a' ] ; then
if ! id $2 &> /dev/null ; then
useradd $2 &> /dev/null
echo $2 | passwd --stdin $2 &> /dev/null
echo "User $2 created succesfully and password changed to it's username."
else
echo "$2 exists already."
fi
elif [ $1 == '-d' ] ; then
if id $2 &> /dev/null ; then
userdel -r $2 &> /dev/null
echo "User $2 delete finished."
else
echo "$2 does not exist yet."
fi
else
echo "Usage: $(basename $0) -a USERNAME | -d USERNAME"
exit 6
fi
改進版:使用read命令;
#!/bin/bash
#
if [ $# -ne 1 ] ; then
echo "Make sure provide ONE argument."
exit 5
fi
if [ $1 == '-a' ] ; then
read -p "Please input a username for creating: " USERNAME
if ! id $USERNAME &> /dev/null ; then
useradd $USERNAME &> /dev/null
echo $USERNAME | passwd --stdin $USERNAME &> /dev/null
echo "User $USERNAME created succesfully and password changed to it's username."
else
echo "$USERNAME exists already."
fi
elif [ $1 == '-d' ] ; then
read -p "Please input a username for deleting: " USERNAME
read -t 5 -p "confirm? Input 'yes' to continue: " CHOICE
[ -z $CHOICE ] && CHOICE='no'
if [ $CHOICE == 'yes' ] ; then
if id $USERNAME &> /dev/null ; then
userdel -r $USERNAME &> /dev/null
echo "User $USERNAME delete finished."
else
echo "$USERNAME does not exist yet."
fi
else
echo
echo "$USERNAME is not deleted."
fi
else
echo "Usage: $(basename $0) { -a | -d }"
exit 6
fi
寫腳本解決問題:
1.判斷用戶通過命令行給的一個參數是否爲整數。
#!/bin/bash
if [ $# -ne 1 ] ; then
echo "Make sure provide ONE digit."
exit 5
fi
if [[ $1 =~ ^[[:digit:]]+$ ]] ; then
echo "$1 is a pure digit."
else
echo "$1 is not a digit."
fi
循環執行結構:
循環:將某一段代碼或命令重複執行0,1或多次;
一個好的循環結構,必須要包括兩個重要的環節:
1.進入循環的條件:
在符合要求或滿足條件時纔開始循環;
2.退出循環條件:
達到某個要求或符號某個條件時需要結束或終止循環的執行;
for循環:
1.遍歷列表的循環:
for 名稱 [in 詞語 ... ] ; do 命令; done
爲列表中的每個成員執行命令。
建議在腳本中的書寫格式:
for VAR_NAME in LIST ; do
循環體
done
或
for VAR_NAME in LIST
do
循環體
done
注意:
VAR_NAME:任意指定的變量名稱,變量的值是從LIST中遍歷獲取的各個元素;
LIST:for循環需要遍歷的列表,可以通過如下方式生成列表:
1.直接給出列表;
2.純整數列表:
1):花括號展開:
{FIRSTNUM..LASTNUM}
2):seq命令
seq [OPTION]... LAST seq 10
seq [OPTION]... FIRST LAST seq 2 6
seq [OPTION]... FIRST INCREMENT LAST seq 0 2 20
3.花括號展開
{FIRST..LAST}
4.命令的執行結果:
ls /etc
5.GLOBBING通配符
6.某些特殊變量的值:
$*,$@
循環體:
一般來說,循環體中應該包括能夠用到VAR_NAME變量的值的命令或命令的組合;如果循環體中的命令沒有用到
VAR_NAME變量的值的話,列表的元素的個數就是此次for循環的次數;
#!/bin/bash
for i in {1..50} ; do
sum=$[sum+i]
done
echo "$sum"
例子:給腳本傳遞三個整數,要求:
1) 如果用戶傳遞過來的不是三個參數,報告正確用法;
2) 如果三個參數中有非純數字字符串,報告錯誤並提示用戶輸入數字;
3) 從三個整數中的選出最大數和最小數,並顯示出來;
4) 不能使用sort命令等排序;
[root@localhost ~]# vim a.sh
#!/bin/bash
#
if [ $# -ne 3 ] ; then
echo " please provide three argument"
exit 5
fi
if [[ $1 =~ [^[:digit:]] ]]||[[ $2 =~ [^[:digit:]] ]] ||[[ $3 =~ [^[:digit:]] ]] ; then
echo "exist error"
fi
if [ $1 -ge $2 ] ; then
if [ $1 -ge $3 ] ; then
echo "the largest is $1"
fi
fi
if [ $2 -ge $1 ] ; then
if [ $1 -ge $3 ] ; then
echo "the largest is $2"
fi
fi
if [ $3 -ge $1 ] ; then
if [ $3 -ge $2 ] ; then
echo "the largest is $3"
fi
fi
if [ $1 -le $2 ] ; then
if [ $1 -le $3 ] ; then
echo "the smallest is $1"
fi
fi
if [ $2 -le $1 ] ; then
if [ $2 -le $3 ] ; then
echo "the smallest is $3"
fi
fi
if [ $3 -le $1 ] ; then
if [ $3 -le $2 ] ; then
echo "the largest is $3"
fi
fi
結果:
[root@localhost ~]# bash a.sh 12 13 14
the largest is 14
the smallest is 12
例:給腳本傳遞一個整數,分別計算該整數以內所有偶數之和以及奇數之和。
[root@localhost ~]# vim q.sh
#!/bin/bash
declare i sum=0 sum1
if [[ $1 =~ [^[:digit:]] ]] ;then
echo "$1should be an integer"
exit 5
fi
if [ "$1" -eq 1 ] ; then
echo "奇數和爲1"
exit 5
fi
if [ "$1" -eq 2 ] ; then
echo "偶數和爲2"
exit 5
fi
for i in $(seq 1 2 $1) ; do
sum=$[sum+i]
done
echo "奇數和爲$sum"
for i in $(seq 2 2 $1 ) ; do
sum1=$[sum1+i]
done
echo "偶數的和爲$sum1"
結果:[root@localhost ~]# bash q.sh 3
奇數和爲4
偶數的和爲2
[root@localhost ~]# bash q.sh 5
奇數和爲9
偶數的和爲6
例:
8.利用RANDOM變量隨機生成十個數字,顯示出這十個數字,並顯示出其中的最大值和最小值。
[root@localhost ~]# vim 000
#!/bin/bash
touch ytc
for i in $( seq 1 10 ) ; do
echo $RANDOM &>> ytc
done
sort -n ytc| head -1
sort -n ytc| tail -1
rm -r ytc
結果:
[root@localhost ~]# bash 000
7119
28639
[root@localhost ~]# bash 000
412
31953
例:給腳本傳遞一個數字作爲行總數,分別打印由*組成的最小銳角朝上和朝下的等腰三角形以及菱形。
角朝下
[root@localhost ~]# vim 9
#!/bin/bash
if [ $# -ne 1 ] ; then
echo " usage: $(basename $0) integer"
exit 5
fi
if [[ $1 =~ [^[:digit:]] ]] ; then
echo " usage: $(basename $0) integer"
exit 5
fi
linenum=$1
for i in $(seq $linenum) ; do
for j in $(seq $[i-1 ]); do
echo -n " "
done
for k in $(seq $[2(linenum-i)+1 ]) ; do
echo -n ""
done
echo
done
結果:[root@localhost ~]# bash 9 8
*******
*****
***
*
角朝上:
[root@localhost ~]# vim 10
#!/bin/bash
if [ $# -ne 1 ] ; then
echo "Usage: $(basename $0) INTEGER"
exit 5
fi
if [[ $1 =~ [^[:digit:]] ]] ; then
echo "Usage: $(basename $0) INTEGER"
exit 6
fi
linenum=$1
for i in $( seq $linenum) ; do
for j in $( seq $[linenum-i]) ; do
echo -n " "
done
for k in $(seq $[2i-1]) ; do
echo -n ""
done
echo
done
結果:
[root@localhost ~]# bash 10 8
*
*****
*******
菱形:
[root@localhost ~]# vim 11
#!/bin/bash
if [ $# -ne 2 ] ; then
echo "Usage: $(basename $0) INTEGER"
exit 5
fi
if [[ $1 =~ [^[:digit:]] ]] ; then
echo "Usage: $(basename $0) INTEGER"
exit 6
fi
linenum=$1
for i in $( seq $linenum) ; do
for j in $( seq $[linenum-i]) ; do
echo -n " "
done
for k in $(seq $[2i-1]) ; do
echo -n ""
done
echo
done
linenum=$2
for i in $(seq $linenum) ; do
for j in $(seq $[i ]); do
echo -n " "
done
for k in $(seq $[2(linenum-i)+1 ]) ; do
echo -n ""
done
echo
done
結果:
[root@localhost ~]# bash 11 8 7
*
*****
*******
*******
*****
***
*
例:分別打印順序和旋轉的九九乘法表。
1)順序的九九乘法表是正常的九九乘法表;
[root@localhost ~]# vim 8
#!/bin/bash
for i in {1..9} ; do
for j in $(seq $i) ; do
echo -ne "$i×$j=$[i*j]\t"
done
echo
done
2)旋轉的九九乘法表是第一行是1×1=1 1×2=2 1×3=3 1×4=4 ... 1×9=9; 第二行是2×2=4 2×3=6 2×4=8 ... 2×9=18; ... 第九行是9×9=81;
[root@localhost ~]# vim 7
#!/bin/bash
for i in {1..9} ; do
for j in $(seq $i 9) ; do
echo -ne "$i×$j=$[i*j]\t"
done
echo
done
結果:
[root@localhost ~]# bash 7
1×1=1 1×2=2 1×3=3 1×4=4 1×5=5 1×6=6 1×7=7 1×8=8 1×9=9
2×2=4 2×3=6 2×4=8 2×5=10 2×6=12 2×7=14 2×8=16 2×9=18
3×3=9 3×4=12 3×5=15 3×6=18 3×7=21 3×8=24 3×9=27
4×4=16 4×5=20 4×6=24 4×7=28 4×8=32 4×9=36
5×5=25 5×6=30 5×7=35 5×8=40 5×9=45
6×6=36 6×7=42 6×8=48 6×9=54
7×7=49 7×8=56 7×9=63
8×8=64 8×9=72
9×9=81
總結:
1.進入循環的條件:LIST中還有未被取盡的元素;
2.退出循環的條件:LIST中的元素被取盡;
3.for循環幾乎不會出現死循環;
4.在執行循環的過程中,需要將整個LIST載入內存,因此對於大列表來說,可能會消耗較多的內存及CPU資源;
編程思想:
將人類的自然語言轉換成程序的代碼語言的方式;
DevOPS
不會開發的運維是沒有出路的。
流程圖
如果 a大於3, 那麼
創建一個用戶
fi