1. 程式人生 > >朗科實習期間筆記心得(十)

朗科實習期間筆記心得(十)

網絡 基礎 運維

bash腳本編程

case選擇分支結構
case 詞 in [模式 [| 模式]...) 命令 ;;]... esac
在腳本中使用的case結構:
case $(VAR-NAME) in
PATTERN1)
COMMAND
...
;;
PATTERN2)
COMMAND
...
;;
...
esac

PATTERN可以是下列幾類字符:
1.普通文本字符
2.GLOBBING風格的通配符
*:任意長度的任意字符
?:任意單個字符
[]:指定範圍內的任意單個字符
[^]:指定範圍以外的任意單個字符
3. | 或字符

eg: 寫一個腳本:
判斷用戶利用腳本刪除某文件時,是否執行刪除操作;
#!/bin/bash
#
if [ -e $1 ] ; then
echo -e "\033[5;1;31mDanger!\033[1;31mAre you sure to delete it? [yes or no] \033[0m"
read -t 5 CHOICE
[ -z $CHOICE ] && CHOICE=no
case $CHOICE in
yes)
rm -rf $1
;;
no)
echo "Right choice."
;;
esac
else
echo "$1 does not exist."
fi

#!/bin/bash
#
if [ -e $1 ] ; then
echo -e "\033[5;1;31mDanger!\033[0m\033[1;31mAre you sure to delete it? [yes or no] \033[0m"
read -t 5 CHOICE
[ -z $CHOICE ] && CHOICE=no
if [ "$CHOICE" == 'yes' ] ; then
rm -rf $1
elif [ "$CHOICE" == 'no' ] ; then
echo "Right choice."
fi
else
echo "$1 does not exist."
fi




if的多分支結構和case的選擇分支結構的異同:
相同點:
1.判斷的條件為真時才會執行對應分支中的語句;條件為假則不執行;
2.都可以設置默認分支語句;即:所有給定的條件的判斷結果都為假時,執行的語句;
不同點:
1.if是根據命令的執行狀態返回值的真假來判斷是否執行某個分支中的語句;
case是根據某個變量中保存的值與指定模式匹配的結果為真假來判斷是否執行某個分支中的語句;
2.if的每個分支中無需單獨的結束標記,case的每個分支都必須以;;結束;


編寫管理用戶賬戶的腳本,第四版,利用case語句+for循環,同時接受創建和刪除用戶的操作;

#!/bin/bash
#
if [ $# -lt 1 ] ; then
echo -e "Usage: $(basename $0) options... USERLIST\n"
echo -e " Options: "
echo -e " -a, --add: \vAdd some users from USERLIST."
echo -e " -d, --delete: \vDelete some users from USERLIST."
echo -e " -h, --help: \vPrint help informationn."
echo -e " -v, --verbose: \vPrint more informationn about manage users."
echo
echo -e " USERLIST FORMAT: "
echo -e " USERNAME1,USERNAME2,...,USERNAMEN"
exit 5
fi

ADDUSER=0
DELUSER=0
DEBUG=0

for I in $(seq $#) ; do
if [ $# -ne 0 ] ;then
case $1 in
-h|--help)
echo -e "Usage: $(basename $0) options... USERLIST\n"
echo -e " Options: "
echo -e " -a, --add: \vAdd some users from USERLIST"
echo -e " -d, --delete: \vDelete some users from USERLIST"
echo -e " -h, --help: \vPrint help informationn"
echo -e " -v, --verbose: \vPrint more informationn about manage users."
echo
echo -e " USERLIST FORMAT: "
echo -e " USERNAME1,USERNAME2,...,USERNAMEN"
exit 0
;;
-v|--verbose)
DEBUG=1
shift
;;
-a|--add)
ADDUSERLIST=$2
ADDUSER=1
shift 2
;;
-d|--delete)
DELUSERLIST=$2
DELUSER=1
shift 2
;;
*)
echo -e "Usage: $(basename $0) options... USERLIST\n"
echo -e " Options: "
echo -e " -a, --add: \vAdd some users from USERLIST"
echo -e " -d, --delete: \vDelete some users from USERLIST"
echo -e " -h, --help: \vPrint help informationn"
echo -e " -v, --verbose: \vPrint more informationn about manage users."
echo
echo -e " USERLIST FORMAT: "
echo -e " USERNAME1,USERNAME2,...,USERNAMEN"
exit 6
;;
esac
fi
done

if [ $ADDUSER -eq 1 ] ; then
for J in $(echo $ADDUSERLIST | tr ',' ' ') ; do
if ! id $J &> /dev/null ; then
useradd $J &> /dev/null
echo $J | passwd --stdin $J &> /dev/null
[ $DEBUG -eq 1 ] && echo "Create user $J successfully."
else
echo "$J exist already."
fi
done
fi

if [ $DELUSER -eq 1 ] ; then
for J in $(echo $DELUSERLIST | tr ',' ' ') ; do
if id $J &> /dev/null ; then
userdel -r $J &> /dev/null
[ $DEBUG -eq 1 ] && echo "Delete user $J finished."
else
echo "$J does not exist yet."
fi
done
fi


while循環結構
while 命令; do 命令; done
在腳本中可以寫成下列結構:
while CONDITION ; do
COMMAND
done

while循環進入循環的條件:CONDITION為真;
while循環退出循環的條件:CONDITION為假;



until循環結構
until 命令; do 命令; done
在腳本中可以寫成下列結構:
until CONDITION ; do
COMMAND
done
until循環進入循環條件:CONDITION為假
until循環推出循環條件:CONDITION為真


註意:
1.while CONDITION 相當於 until !CONDITION
2.while和until循環結構中,沒有變量自增或自減的變化方法;因此需要使用語句手動給出變量的變化方式;


寫一個腳本,使用while或until循環,計算100以內整數的和;
#!/bin/bash
#
declare -i I=0
until [ $I -eq 100 ] ; do
let I++
let SUM+=$I
done

echo $SUM

#!/bin/bash
#
declare -i I=0
while [ $I -lt 100 ] ; do
let I++
let SUM+=$I
done

echo $SUM


循環控制語句:
continue:
continue [n]
繼續 for、while、或 until 循環。
提前結束第N層當前循環,直接進入下一輪條件判斷,如果條件判斷結果仍然滿足循環進入條件,則開啟下一輪循環;
break
break [n]
退出 for、while、或 until 循環
提前結束第N層循環,且不再繼續後續循環


while和until的兩種特殊循環使用方法:
1.無限循環方法
while true ; do
COMMAND
done

until false ; do
COMMAND
done


猜數字遊戲:
#!/bin/bash
#
NUMBER=$[RANDOM%100+1]
while true ; do
read -p "Input a number: " INPTNUM
if [ $INPTNUM -gt $NUMBER ] ; then
echo "Too big"
elif [ $INPTNUM -lt $NUMBER ] ; then
echo "Too small"
else
echo "Yes! you WIN. That's $NUMBER."
break
fi
done

#!/bin/bash
#
NUMBER=$[RANDOM%100+1]
until false ; do
read -p "Input a number: " INPTNUM
if [ $INPTNUM -gt $NUMBER ] ; then
echo "Too big"
elif [ $INPTNUM -lt $NUMBER ] ; then
echo "Too small"
else
echo "Yes! you WIN. That's $NUMBER."
break
fi
done


註意:在此類循環結構中需要適當的添加continue或break,使無限循環可控;


2.實現遍歷功能的while和until循環結構
while read LINES ; do
COMMADN
done < /PATH/FORM/SOMEFILES

until ! read LINES ; do
COMMADN
done < /PATH/FORM/SOMEFILES

註意:在做遍歷循環時建議使用for;

select循環結構
select NAME [in 詞語 ... ;] do 命令; done
select循環也是一種遍歷列表的方式創建一個可視化菜單,每個列表中項都有一個數字編號與之對應,供用戶選擇使用,而用戶只需要選擇編號即可;
select是一種默認無限循環結構;因此必須在循環體中衛select提供退出循環條件,通常可以使用break或exit命令實現;
通常情況下,select循環會和case一起使用,以進行合理的取值判斷;

在腳本中實現格式:
select VAR-NAME in LIST ; do
COMMAND
done
寫一個腳本,顯示以/bin/bash為默認shell的用戶的ID信息;
#!/bin/bash
#
select I in $(awk -F : '/\/bin\/bash$/{print $1}' /etc/passwd) quit ; do
case $I in
quit)
exit
;;
*)
echo "The UID of $I is $(id -u $I)"
;;
esac
done


bash腳本編程--函數
對於bash來說,函數就是由命令和語句結構構成的能夠實現特定功能集合;

為什麽要用函數?
在bash腳本編寫過程中,有可能會出現重復且不做任何改變的代碼內容,如果這類內容完全依靠原始代碼書寫的話,不易於排錯和優化;因此,可以選擇將此類代碼封裝在函數中,在適當的場景中可以重復調用執行;

像此類被封裝起來的代碼塊,通常稱其為模塊,也可以稱為函數;

註意:1.想要使用函數,必須在使用前先定義;
2.如果在某個bash腳本中包含了函數體,默認函數體中的各命令和語句不會被執行的;只有在調用函數名的時候,才會執行函數體中的命令和語句;
3.通常需要重復執行的代碼塊或命令集,可以封裝成函數;
4.被調用的函數只能在調用函數的shell中被執行;

定義函數的方法:
函數由兩部分組成:
函數名 + 函數體
函數名:調用函數時所使用的字符串標識;在一個執行環境中,函數名不允許重復定義;
函數體:能夠實現特定獨立功能的shell命令或結構化語句塊;

定義的語法:
語法一:
function function-name {
func-body
}
語法二:
func-name() {
func-bady
}
註意:在語法二的格式中,func-name和()之間絕對不能有空格;

註意:函數可以在腳本中定義,也可以在當前shell中通過交互式環境定義;


函數的使用:
函數在定義的時候,其函數體中包含的所有命令或結構化語句都不會被執行;只有在函數被調用時,才能執行其函數體中的各命令和語句;

調用方式:在命令行或腳本中,通過直接給出函數名的方式進行函數調用;

通常可以將常用的函數存放在專門用於保存函數的文件中;如果想調用這個文件中已經被定義保存的函數時;只需要在命令行或腳本中使用source命令(.命令)加載文件內容到當前shell中,然後再直接使用函數名調用函數即可;

函數的撤銷:unset命令
格式:# unset func-name
註意:可以使用set命令查看當前已經定義生效的函數;


函數的返回值:
兩種返回值:
函數的執行結果返回值:
1.在函數體中所添加的命令有標準輸出;
2.在函數體中使用echo或printf命令強制輸出返回信息;
函數的執行狀態返回值:
1.默認情況下,其狀態返回值為函數體中最後一條命令的狀態返回值;
2.自定義狀態返回值(退出碼):
return命令
return [n]
從一個 shell 函數返回。 n: 0-255(1.2.127)為系統保留的狀態碼;盡量不用;
註意:在函數被調用執行時,一旦遇到return命令,則不會再繼續執行函數體中其他的後續命令,立刻結束次此函數的調用執行;

函數的生命周期:
一般來講,從函數被調用時開始,直到函數體中所有的命令和結構化語句全部執行完成,或者遇到return命令,則函數的調用結束;

函數的實參:
對於bash的函數來說,沒有形參,只有實參;
bash函數的實參是使用$1,$2...位置變量提供數據的;
可以使用$@,$*表示全部的參數列表;
可以使用$#計算參數的個數;

註意:為函數提供參數時使用的位置變量,是調用函數名時在函數名後面的對應位置上的參數信息;與腳本位置參數不是一回事;

變量:函數被調用時必須在某特定的shell中被調用,因此,函數中可以繼承並識別出環境變量和由調用函數shell定義的本地變量;

在函數中還可以定義一類局部變量:而局部變量僅在函數的生命周期內有效,因此在結束函數執行之前,應該撤銷所有該函數定義的局部變量;

局部變量的定義方式:
local VAR-NAME=值


變量的替換方式:
前提:定義環境變量
#!/bin/bash
#
testvar() {
local MYVAR=chinalink
echo "Internal function: $MYVAR"
}
echo "Global variables: $MYVAR"
MYVAR=link
echo "External function, $MYVAR"
testvar


函數的遞歸調用:
廣義:在一個函數中調用另一個函數;
狹義:在函數體中調用函數自身;
直接調用:func1(){
func1
}
間接調用:func2(){
func1
}
func1(){
func2
}

函數直接遞歸調用示例1:
計算某個數字的階乘;
利用for循環:
#!/bin/bash
#
fact=1
if [ $1 -eq 0 ] ; then
echo "0! is $fact"
elif [ $1 -eq 1 ] ; then
echo "1! is $fact"
else
for I in $(seq $1) ; do
let fact=$[fact*$I]
done
echo "${1}! is $fact"
fi

利用函數遞歸調用:
#!/bin/bash
#
fact(){
if [ $1 -eq 0 ] || [ $1 -eq 1 ] ; then
echo 1
else
echo "$[$1*$(fact $[$1-1])]"
fi
}

echo "${1}! is $(fact $1)"

示例2:
斐波那契數列(黃金分割數列)

1 1 2 3 5 8 13 21 34 55 ...

N=N-1 + N-2

#!/bin/bash
#
fabonacci(){
if [ $1 -eq 1 ] || [ $1 -eq 2 ] ; then
echo -n "1 "
else
echo -n "$[$(fabonacci $[$1-1])+$(fabonacci $[$1-2])] "
fi
}

for I in $(seq $1) ; do
fabonacci $I
done
echo

示例三:
漢諾塔

#!/bin/bash
#
step=0
move(){
let step++
echo "$step: move disk $1 $2 --> $3"
}

hanoi(){
if [ $1 -eq 1 ] ; then
move $1 $2 $4
else
hanoi "$[$1-1]" $2 $4 $3
move $1 $2 $4
hanoi "$[$1-1]" $3 $2 $4
fi
}

hanoi $1 A B C

朗科實習期間筆記心得(十)