1. 程式人生 > >bash腳本編程

bash腳本編程

腳本編程

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腳本編程