bash腳本編程
bash腳本編程語言:
腳本類語言
解釋性語言
過程式編程語言
過程式編程語言的結構:
順序執行結構:默認
從上到下,自左而右執行所有的語句(命令)
選擇執行結構:
當條件滿足或不滿足時才會執行對應的語句(命令)
循環執行結構:
重復執行某段語句(命令)
bash腳本編程語言中也具備上述結構:
順序執行結構:默認
選擇執行結構:
根據給定的條件邏輯判斷結構或根據某個可選取的取值範圍,進而選擇某個分支結構中的命令語句予以執行的方式;
if:
選擇執行結構的標準,根據條件的邏輯判斷結果選擇執行的語句內容;
case:
選擇執行結構的標準,根據符合某特定範圍取值標準來選擇執行的語句內容;
循環執行結構:
對於特定語句內容,重復執行0次,1次或多次;
for:以遍歷列表的方式來進行循環;
while:根據給定條件的邏輯判斷結果進行循環;邏輯判斷結果為真,才循環;否則,停止循環;
until:根據給定條件的邏輯判斷結果進行循環;邏輯判斷結果為假,才循環;否則,停止循環;
select:死循環,即沒有默認退出條件的循環;利用循環提供一個可選擇的列表;
bash腳本的執行結構值if選擇執行結構:
if語句:
if 命令; then 命令; [ elif 命令; then 命令; ]... [ else 命令; ] fi
1.if語句的單分支結構:
if 命令; then 命令; fi
註意:是否會執行then後面的命令,取決於if後面的命令的執行狀態返回值;
1.如果其返回值為真,則執行then後面的命令;
2.如果其返回值為假,則不執行then後面的命令;
建議在腳本中的書寫格式:
if CONDITION ;then
STATEMENT
...
fi
或者
if CONDITION
then
STATEMENT
...
fi
2.if語句的雙分支結構:
if 命令; then 命令; [ else 命令; ] ;fi
註意:是否會執行then後面的命令,取決於if後面的命令的執行狀態返回值;
1.如果其返回值為真,則執行then後面的命令;
2.如果其返回值為假,則不執行else後面的命令;
建議在腳本中的書寫格式:
if CONDITION ;then
STATEMENT
...
else
STATEMENT
...
fi
或者
if CONDITION
then
STATEMENT
...
else
STATEMENT
...
fi
3.if語句的多分支結構:
if 命令; then 命令; [ elif 命令; then 命令; ]... [ else 命令; ] fi
註意:是否會執行then後面的命令,取決於if後面的命令的執行狀態返回值或elif和面的命令的執行狀態返回值;
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
fi
else
STATEMENT
...
fi
else
STATEMENT
...
fi
示例:
1.判斷某個用戶的默認登錄shell是否為/bin/bash;
#!/bin/bash # USERNAME=$(cut -d : -f 1 /etc/shadow | sort -R | head -1) USERSHELL=$(egrep "^$USERNAME\>" /etc/passwds | cut -d : -f 7) if [ "$USERSHELL" == "/bin/bash" ] ; then echo "${USERNAME}'s login shell is /bin/bash." fi unset USERNAME USERSHELL
2.判斷某個用戶的默認登錄shell是否為/bin/bash;如果不是就顯示它的登錄shell;
#!/bin/bash # USERNAME=$(cut -d : -f 1 /etc/shadow | sort -R | head -1) USERSHELL=$(egrep "^$USERNAME\>" /etc/passwd | cut -d : -f 7) if [ "$USERSHELL" == "/bin/bash" ] ; then echo "${USERNAME}'s login shell is /bin/bash." else echo "${USERNAME}'s login shell is ${USERSHELL}." fi
bash腳本編程之用戶交互使用:
位置參數變量:
$0:命令的本身,對於腳本而言,就是該腳本的路徑;
$1,$2,...$N:腳本後面通過命令行給腳本傳遞的命令行參數;
N>9時,引用該位置變量時需要加{},即:${10}; $10,只會認為其是$1;
#!/bin/bash # #USERNAME=$(cut -d : -f 1 /etc/shadow | sort -R | head -1) USERSHELL=$(egrep "^$1\>" /etc/passwds | cut -d : -f 7) if [ "$USERSHELL" == "/bin/bash" ] ; then echo "${1}'s login shell is /bin/bash." fi
特殊變量:
$@:給出的所有位置參數的列表,當使用雙引號引用時,每個參數作為單獨的字符串存在;
$*:給出的所有位置參數的列表,當使用雙引號引用時,整個參數列表被當做一個字符串;
$#:表示除去$0之外,整個命令行中有多少個參數;
read命令:
Read a line from the standard input and split it into fields.
read [-ers] [-a 數組] [-p 提示符] [-t 超時] [-u 文件描述符] [名稱 ...]
-a array:定義索引數組;
-p promt:給用戶輸出提示信息;
-t timeout:用戶輸入的超時時間;
name:變量或數組的名稱;如果省略此內容,bash會將read讀到的信息直接保存到內置的名為REPLY
變量中;
註意:
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
改進版:
#!/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
判斷用戶通過命令行給的一個參數是否為整數。
#!/bin/bash # if [ $# -ne 1 ] ; then echo "確定這是一個參數。" exit 4 fi if ! [[ $1 =~ [^[:digit:]] ]] &> /dev/null ; then echo "這是一個整數。" else echo "這不是一個整數。" fi
循環執行結構:
循環:將某一段代碼或者命令重復執行0次,1次或多次;
一個好的循環結構,必須要包括兩個重要的環節;
1.進入循環的條件:
在符合要求或滿足條件時才開始循環;
2.退出循環的條件:
達到某個要求或符號某個條件時需要結束或終止循環的執行;
for循環:
1.遍歷列表的循環:
Execute commands for each member in a list.
格式:
for NAME [in WORDS ... ] ; do COMMANDS; 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}
{FIRST,SECIND,THIRD,...LAST}
2)seq命令:
seq [OPTION]... LAST
seq [OPTION]... FIRST LAST
seq [OPTION]... FIRST INCREMENT LAST
3.花括號展開:
{FIRST..LAST} (字符)
4.命令的執行結果:
ls /etc
grep /PATH/TO/SOMEFILE
5.GLOBBING
6.某些特殊變量的值:
$*,&@
循環體:
一般來說,循環體中應該能夠用到VAR_NAME變量的值的命令或命令的組合;如果循環體中的命令並沒有用到VAR_NAME變量的值得話,列表的元素個數就是此次for循環的次數;
使用for循環,計算1到100的數字之和:
#!/bin/bash # for I in {1..100} 或者 $(seq 1 100}; do SUM=$[SUM+I] 或者 let SUM+=$I done echo "The summary is: $SUM:"
1到100所有奇數的和
#!/bin/bash # for I in {1..100..2} 或者${seq 1 2 100}; do SUM=$[SUM+I] 或者 let SUM+=$I done echo "The summary is: $SUM:"
0到100所有偶數的和
#!/bin/bash # for I in {0..100..2} 或者${seq 0 2 100}; do SUM=$[SUM+I] 或者 let SUM+=$I done echo "The summary is: $SUM:"
寫一個腳本,能夠通過-a或-d選項添加或刪除一個或多個用戶賬戶;
#!/bin/bash # if [ $# -lt 2 ] ; then echo "Make sure provide more than TWO arguments." exit 5 fi if [ $1 == '-a' ] ; then shift for I in "$@" ; do if ! id $I &> /dev/null ; then useradd $I &> /dev/null echo $I | passwd --stdin $I &> /dev/null echo "User $I created succesfully and password changed to it's username." else echo "$I exists already." fi done elif [ $1 == '-d' ] ; then shift for J in "$@" ; do if id $J &> /dev/null ; then userdel -r $J &> /dev/null echo "User $J delete finished." else echo "$J does not exist yet." fi done else echo "Usage: $(basename $0) -a UNAME1 [UNAME2 ...] | -d UNAME1 [UNAME2 ...]" exit 6 fi
總結:
1.進入循環的條件:LIST中尚有未被取盡的元素;
2.退出循環的條件:LIST中的元素被取盡;
3.for循環幾乎不會出現死循環;
4.在執行循環的過程中,需要將整個LIST載入內存,因此,對於大列表來說,可能會消耗較多的內存及CPU資源;
指定指定範圍內自然數的和:
#!/bin/bash # declare -i SUM=0 read -p "Please input TWO integer: " INT1 INT2 if [[ $INT1 =~ [^[:digit:]] ]] ; then echo "$INT1 must be an integer." exit 5 fi if [[ $INT2 =~ [^[:digit:]] ]] ; then echo "$INT2 must be an integer." exit 5 fi if [ $INT1 -gt $INT2 ] ; then for I in $(seq $INT2 $INT1) ; do # SUM=$[SUM+I] let SUM+=$I done echo "The summary is: $SUM" else for I in $(seq $INT1 $INT2) ; do # SUM=$[SUM+I] let SUM+=$I done echo "The summary is: $SUM" fi
打印有"*"組成的倒置的等腰三角形;
********* 1行,0個空白字符,9個"*"
******* 2行,1個空白字符,7個"*"
***** 3行,2個空白字符,5個"*"
*** 4行,3個空白字符,3個"*"
* 5行,4個空白字符,1個"*"
N行,N-1個空白字符,2*(總行數-當前行數)+1個"*"
#!/bin/bash # 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
打印九九乘法表:
第一行:1個
第二行:2個
...
第九行:9個
#!/bin/bash # for I in {1..9} ; do for J in $(seq $I) ; do echo -ne "$I×$J=$[I*J]\t" done echo done
#!/bin/bash # for (( I=1 ; I<=9 ; I++ )) ; do for (( J=1 ; J<=I ; J++ )) ; do echo -ne "$J×$I=$[I*J]\t" done echo done
以上兩個例子,均使用for循環的嵌套;往往需要兩層的循環嵌套才能打印平面效果;外層的for循環,負責控制行數輸出;內層的for循環,負責每一行中各個 列的輸出;
2.通過控制變量實現for循環;
for ((: for (( exp1; exp2; exp3 )); do COMMANDS; done
可以在腳本中攜程如下格式:
for ((: for (( exp1; exp2; exp3 )) ; do
COMMANDS
done
exp1:表達式1,為指定的變量賦初始值;
exp2:表達式2,此次循環的退出條件;
exp3:表達式3,指定的變量值得變化規律;
計算從1到100的自然數的和:
#!/bin/bash # for(( i=1 ; I<=100 ; I++ )) ; do let SUM+=$I done echo "The summary is :$SUM"
bash腳本編程