1. 程式人生 > 其它 >linux之Shell詳述

linux之Shell詳述

目錄

Shell

Shell是一門程式語言。學習Shell的目的是提高(批量)執行多條命令的效率。

1、學習Shell之前需要了解的問題

1.1 編譯型語言和解釋型語言

編譯型語言:需要編譯器將程式碼編譯成二進位制檔案,例如:C語言、Java語言、Golang語言。
解釋型語言:需要解析器在程式碼需要執行的時候將程式碼編譯成二進位制資料,例如:Python、Shell。

1.2 強一致性和弱一致性

強一致性是變數必須跟設定資料型別保持一致。
弱一致性是變數可以根據不同場景自動變換自己的資料型別。

2、Shell語言入門

2.1 Shell代表的兩層意思

1.shell指的是它本身這門語言。

2.伺服器中的命令解析器,例如:bash、sh
[root@mysql01 ~]# chsh -l  # 檢視伺服器已安裝的解析器
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash

2.2 Shell程式書寫規範

# 注意:
1.shell指令碼通常以`.sh`結尾
2.需要在shell指令碼的第一行指定解析器

  #!/bin/bash
  命令

2.3 Shell中的變數

# 什麼是變數
量:指的是記錄事物的狀態。
變:指的是事物的狀態可以發生改變。

# 為什麼要使用變數?
程式執行過程中本質上就需要一些列的變化。

# 變數的使用原則:
先定義,後使用。

2.3.1 定義:變數名=值

[root@dxd ~]# name="張飛"

# 注意:等號兩邊不能有空格(在shell解析器中,認為空格代表分隔符)

2.3.2 引用:使用$符取值

[root@dxd ~]# name="張飛"
[root@dxd ~]# echo $name
張飛

2.3.3 刪除:unset 變數名

[root@dxd ~]# echo $name
張飛
[root@dxd ~]# unset name
[root@dxd ~]# echo $name

2.3.4 變數的命名規則

1.以字母開頭:name
2.使用中劃線或者下劃線做單詞之間的連結:city_name;city-name
3.同類型的變數用數字區分:name1;name2
4.變數名區分大小寫,大小寫不同的話就不是同一個變數:student;Student

2.3.5 變數值的來源

# 1.直接賦值
[root@dxd ~]# name="張飛"

# 2.從位置引數中獲取:例如在指令碼執行時,可以在執行指令碼後面跟引數
[root@dxd ~]# ./first.sh n m g 3 4 4 4 4 5 5 5 5 5 
n
m
g
5

[root@dxd ~]# vim first.sh 
#!/bin/bash
echo $1
echo $2
echo $3
echo ${11}
[root@mysql01 ~]# chmod +x ./first.sh


# 3.使用者輸入
[root@dxd ~]# ./first1.sh 
請輸入:345
345
[root@dxd ~]# cat first1.sh 
#!/bin/bash

read -p "請輸入姓名:" name
echo $name

2.3.6 系統預定義變數

預定義變數 解釋
$* 代表所有的引數
$@ 所有的引數
$# 引數的個數
$$ 當前程序的PID
$? 代表上一條命令的返回值(預設:0代表成功,其他代表失敗,最大128)
[root@mysql01 ~]# ./first3.sh 0 1 2 3 4 55 66
0 1 2 3 4 55 66
0 1 2 3 4 55 66
7
2607
0

[root@mysql01 ~]# ./first3.sh 0 1 2 3 "4 55 66"
0 1 2 3 4 55 66
0 1 2 3 4 55 66
5
2609
0

[root@mysql01 ~]# cat first3.sh
#!/bin/bash
echo $*
echo $@
echo $#
echo $$
echo $?

2.4 Shell中的常量

相對於變數來說,常量就是不可以變化的值。
[root@dxd ~]# x=100
[root@dxd ~]# readonly x
[root@dxd ~]# echo $x
100
[root@dxd ~]# x=200
-bash: x: readonly variable  # 報錯:提示x為只讀變數

3、Shell中的資料型別

# 在學習shell之前,需要了解的幾個問題
1.什麼是資料?
資料即變數的值,如age=18,18則是我們儲存的資料。

2.為什麼需要這麼多的資料型別?
資料型別就是用來描述變數值(資料變化的型別)的型別。為了儘可能的減少變數值佔用空間的大小,提升空間效率。

3.1 數字型別

 數字型別就是用來儲存數字的。
 
[root@dxd ~]# age=10
[root@dxd ~]# salary=3.1

3.2 字串型別

字串型別就是用來儲存字元的。
# 注意:
雙引號(""):弱引用,保留特殊字元的含義。
單引號(''):強引用,取消特殊字元的含義。

[root@dxd ~]# name='張飛'
[root@dxd ~]# address='上海市青浦區xxx'

[root@dxd ~]# echo "$name 住在 $address"
  張飛 住在 上海市青浦區xxx
  
[root@dxd ~]# echo '$name 住在 $address'
  $name 住在 $address

3.3 陣列

1.什麼是陣列?
陣列就是一系列元素的集合,一個數組內可以存放多個元素。

2.為什麼要用陣列?
為了將多個元素彙總到一起,避免單獨定義。

3.陣列的分類
普通陣列(列表):只能夠使用整數作為索引。
關聯陣列(字典):可以使用字串作為陣列的索引。

3.4 普通陣列

3.4.1 宣告陣列的四種方式

  • 方式一:直接宣告
[root@dxd ~]# array=("張飛", 18)
[root@dxd ~]# declare -a
declare -a array='([0]="張飛," [1]="18")'
  • 方式二:指定索引
[root@dxd ~]# array=([2]=19 [1]="關羽" [0]="未婚")
[root@dxd ~]# declare -a
declare -a array='([0]="未婚" [1]="關羽" [2]="19")'
  • 方式三:依次賦值
[root@dxd ~]# array[0]="已婚"
[root@dxd ~]# array[1]="劉備"
[root@dxd ~]# declare -a
declare -a array='([0]="已婚" [1]="劉備" [2]="19")'
  • 方式四:利用執行命令的結果賦值
[root@dxd ~]# array1=(`ls`)
[root@dxd ~]# declare -a
declare -a array1='([0]="1.txt" [1]="first.sh" [2]="mysql80-community-release-el7-3.noarch.rpm" [3]="nginx-1.20.2" [4]="nginx-1.20.2.tar.gz" [5]="test" [6]="聖誕樹.py")'

3.4.2 訪問陣列的兩種方式

# 注意:正向索引從`0`開始,反向索引從`-1`開始。
  • 方式一:正向索引
[root@dxd ~]# declare -a
declare -a array1='([0]="1.txt" [1]="first.sh" [2]="mysql80-community-release-el7-3.noarch.rpm" [3]="nginx-1.20.2" [4]="nginx-1.20.2.tar.gz" [5]="test" [6]="聖誕樹.py")'

[root@dxd ~]# echo ${array1[2]}
mysql80-community-release-el7-3.noarch.rpm
  • 方式二:反向索引
[root@dxd ~]# declare -a
declare -a array1='([0]="1.txt" [1]="first.sh" [2]="mysql80-community-release-el7-3.noarch.rpm" [3]="nginx-1.20.2" [4]="nginx-1.20.2.tar.gz" [5]="test" [6]="聖誕樹.py")'

[root@dxd ~]# echo ${array1[-5]}
mysql80-community-release-el7-3.noarch.rpm

3.5 關聯陣列

3.5.1 宣告關聯陣列的兩種方式

  • 方式一:賦值宣告
[root@dxd ~]# declare -A info
[root@dxd ~]# info['name']='張飛'
[root@dxd ~]# info['age']=19
  • 方式二:直接宣告
[root@dxd ~]# declare -A info='([name]="關羽" [age]=18)'

3.5.2 訪問關聯陣列

[root@dxd ~]# echo ${info["name"]}
關羽

4、變數值操作

4.1 獲取變數值的長度

語法:${#變數名}
[root@dxd ~]# name='World'
[root@dxd ~]# echo ${#name}
5
[root@dxd ~]# echo $name | wc -L
5
[root@dxd ~]# echo $name | awk '{print length}'
5

4.2 切片

語法:${變數名:開始位置:步長}

[root@dxd ~]# echo ${name:1}  # 從第二個位置往後
orld
[root@dxd ~]# echo ${name:1:2}  # 從第二個位置往後取兩位數
or
[root@dxd ~]# echo ${name::2}  # 從第一個位置開始往後取兩位數
Wo

4.3 截斷

[root@dxd ~]# url='www.baidu.com'
[root@dxd ~]# echo ${url#www.}
baidu.com
[root@dxd ~]# echo ${url#*w.}
baidu.com

------------------

[root@dxd ~]# echo ${url%.com}
www.baidu

4.4 替換

[root@dxd ~]# echo $url
www.baidu.com
[root@dxd ~]# echo ${url/baidu/aliyun}
www.aliyun.com

4.5 變數的替代

${x:-臨時變數資訊}:如果變數不存在,則使用預設值。

[root@dxd ~]# echo $name1

[root@dxd ~]# echo ${name1-"西施"}
西施

[root@dxd ~]# name1="呂布"
[root@dxd ~]# echo ${name1-"西施"}
呂布


${x:=新的變數資訊}:如果變數不存在,則使用預設值,並且將預設值賦值給變數。
[root@dxd ~]# echo $name1

[root@dxd ~]# echo ${name1:="關羽"}
關羽
[root@dxd ~]# echo $name1
關羽

${x:?設定變數提示資訊}:如果執行的變數不存在,則使用預設的提示資訊。
[root@dxd ~]# echo $dfsdfasdfd
-bash: dfsdfasdfd: 未找到命令
[root@dxd ~]# echo ${dsfsdafds:?"該變數未定義"}
-bash: dsfsdafds: 該變數未定義

${x:+有設定變數提示資訊}:變數存在則列印相關資訊
[root@dxd ~]# echo $name1
關羽
[root@dxd ~]# echo ${name1:+該變數存在}
該變數存在
[root@dxd ~]# unset name1
[root@dxd ~]# echo ${name1:+該變數存在}

4.6 let計算

[root@dxd ~]# n=100
[root@dxd ~]# let n=$n+1
[root@dxd ~]# echo $n
101

4.7 取命令的結果賦值給變數

反引號(``)
$():

[root@dxd ~]# pid=`ps -ef | grep 30043 | head -1 | awk '{print $3}'`
[root@dxd ~]# echo $pid
1112

[root@dxd ~]# pid=$(ps -ef | grep 30043 | head -1 | awk '{print $3}')
[root@dxd ~]# echo $pid
1112

5、元字元

元字元是指能夠被Shell解析的特殊字元。

5.1 算數運算子

算數運算子主要是用來做一些簡單運算。

1、運算子

+  :  加法運算
- : 減法運算
* : 乘法運算
/ :  除法運算
% :  取模運算

2、bc運算

[root@dxd ~]# echo `echo 1+1 | bc`
2

# 保留兩位小數
[root@dxd ~]# echo `echo "scale=2;1.1/1.2" | bc`
.91

3、expr運算

[root@dxd ~]# echo `expr 5 / 3`
1

注意:expr只支援整數運算。

4、$(())運算

[root@dxd ~]# echo $((5 / 3))
1

注意:$(())只支援整數運算。

5、$[]運算

[root@dxd ~]# echo $[5/3]
1
注意:$[]只支援整數運算。

6、let

[root@dxd ~]# let res=5/3
[root@dxd ~]# echo $res
1
注意:let只支援整數運算。

5.2 測試運算子

主要是用來探測某一個檔案的屬性的。

1、測試檔案狀態

1.1、測試是否是目錄

[root@dxd ~]# test -d /root/nginx-1.20.2
[root@dxd ~]# echo $?
0
[root@dxd ~]# test -d /root/first.sh 
[root@dxd ~]# echo $?
1

[root@dxd ~]# [ -d /root/nginx-1.20.2 ]; echo $?
0
[root@dxd ~]# [ -d /root/first.sh ]; echo $?
1

1.2、測試檔案是否非空

[root@dxd ~]# cat first.sh 
#!/bin/bash
echo $$
sleep 100
[root@dxd ~]# cat first1.sh 
[root@dxd ~]# [ -s /root/first.sh ];echo $?
0
[root@dxd ~]# [ -s /root/first1.sh ];echo $?
1
[root@dxd ~]# 

1.3、測試檔案是否是標準檔案

[root@dxd ~]# [ -f /root/first.sh ];echo $?
0
[root@dxd ~]# [ -f /root/nginx-1.20.2 ];echo $?
1

1.4、判斷檔案是否可寫

[root@dxd ~]# [ -w /root/1.txt ];echo $?
0
[root@dxd ~]# su - test
最後一次失敗的登入:三 3月  9 15:10:14 CST 2022從 124.221.117.45ssh:notty 上
最有一次成功登入後有 42 次失敗的登入嘗試。
[test@dxd ~]$ [ -w /root/1.txt ];echo $?
1

1.5、判斷檔案是否可讀

[test@dxd ~]$ [ -r /root/1.txt ];echo $?
1
[test@dxd ~]$ exit
logout
[root@dxd ~]#  [ -r /root/1.txt ];echo $?
0

1.6、判斷檔案是否可執行

[root@dxd ~]# [ -x /root/first.sh ];echo $?
0
[root@dxd ~]# [ -x /root/1.txt ];echo $?
1

1.7、判斷是否是軟連線檔案

[root@dxd ~]# [ -L /bin ];echo $?
0
[root@dxd ~]# [ -L /root ];echo $?
1

1.8、判斷是否是一個磁碟檔案

[root@dxd /]# [ -c /proc/tty ];echo $?
1

5.3 字串測試運算子

主要是用來判斷字串是否相等。

1、判斷兩個字串是否相等

[root@dxd /]# [ "aaa" == "bbb" ] ; echo $?
1
[root@dxd /]# [ "aaa" == "aaa" ] ; echo $?
0

2、判斷兩個字串是否不相等

[root@dxd /]# [ "aaa" != "aaa" ] ; echo $?
1

3、判斷字串是否為空

[root@dxd /]# [ -z "" ];echo $?
0
[root@dxd /]# [ -z "123" ];echo $?
1

4、判斷字串是否不為空

[root@dxd /]# [ -n "123" ];echo $?
0
[root@dxd /]# [ -n "" ];echo $?
1

5.4 數值測試運算子

1、數值測試運算子

運算子 解釋
-eq 等於
-ne 不等於
-gt 大於
-lt 小於
-ge 大於等於
-le 小於等於
-a 並且
-o 或者

2、等於(-eq)

[root@dxd ~]# [ 123 -eq 234 ];echo $?
1
[root@dxd ~]# [ 123 -eq 123 ];echo $?
0

3、不等於(-ne)

[root@dxd ~]# [ 123 -ne 234 ];echo $?
0
[root@dxd ~]# [ 123 -ne 123 ];echo $?
1

4、大於(-gt)

[root@dxd ~]# [ 123 -gt 234 ];echo $?
1
[root@dxd ~]# [ 1203 -gt 234 ];echo $?
0

5、小於(-lt)

[root@dxd ~]# [ 1203 -lt 234 ];echo $?
1
[root@dxd ~]# [ 123 -lt 234 ];echo $?
0

6、大於等於(-ge)

[root@dxd ~]# [ 123 -ge 234 ];echo $?
1
[root@dxd ~]# [ 234 -ge 234 ];echo $?
0
[root@dxd ~]# [ 2340 -ge 234 ];echo $?
0

7、小於等於(-le)

[root@dxd ~]# [ 234 -le 123 ];echo $?
1
[root@dxd ~]# [ 234 -le 1230 ];echo $?
0
[root@dxd ~]# [ 234 -le 234 ];echo $?
0

8、並且(-a)

[root@dxd ~]# [ 123 -eq 123 -a 234 -eq 234 ];echo $?
0
[root@dxd ~]# [ 123 -eq 123 -a 234 -eq 2304 ];echo $?
1

9、或者(-o)

[root@dxd ~]# [ 123 -eq 123 -o 234 -eq 2304 ];echo $?
0

5.5 關係運算符

運算子 解釋
< 小於
> 大於
<= 小於等於
>= 大於等於
== 等於
!= 不等於
&& 並且
兩個豎向 或者
; 前後都執行

1、小於

[root@dxd ~]# (($x<100));echo $?
1
[root@dxd ~]# (($x==100));echo $?
0

2、大於

[root@dxd ~]# (($x>100));echo $?
1
[root@dxd ~]# (($x==100));echo $?
0

3、小於等於

[root@dxd ~]# (($x<=100));echo $?
0

4、大於等於

[root@dxd ~]# (($x>=100));echo $?
0

5、等於

[root@dxd ~]# (($x==100));echo $?
0

6、不等於

[root@dxd ~]# (($x!=100));echo $?
1

7、並且

[root@dxd ~]# echo 123 && echo 456
123
456
[root@dxd ~]# echso 123 && echo 456
-bash: echso: 未找到命令

8、或者

[root@dxd ~]# echso 123 || echo 456
-bash: echso: 未找到命令
456
[root@dxd ~]# echo 123 || echo 456
123

9、前後都執行

[root@dxd ~]# echo 123 ; echo 456
123
456
[root@dxd ~]# echso 123 ; echo 456
-bash: echso: 未找到命令
456
[root@dxd ~]# echo 123 ; ecsho 456
123
-bash: ecsho: 未找到命令

5.6 賦值運算子

運算子 解釋
= 直接賦值
+= 先加法後等於
*= 先乘法後賦值
/= 先除法後賦值
%= 先取模後賦值

1、直接賦值

[root@dxd ~]# x=100

2、先加法後等於

[root@dxd ~]# let x+=1
[root@dxd ~]# echo $x
101

3、先乘法後賦值

[root@dxd ~]# let x*=2
[root@dxd ~]# echo $x
202

4、先除法後賦值

[root@dxd ~]# let x/=2
[root@dxd ~]# echo $x
101

5、先取模後賦值

[root@dxd ~]# let x%=3
[root@dxd ~]# echo $x
2

5.7 補充

[[]]: 與 [] 基本上一致,不同的是[[]]支援正則

[test@dxd ~]$ [[ "$USER" =~ ^t ]];echo $?
0
[test@dxd ~]$ [[ "$USER" =~ t$ ]];echo $?
0
[test@dxd ~]$ [ "$USER" =~ t$ ];echo $?
-bash: [: =~: binary operator expected
2

6、流程控制之IF

if是一個條件判斷的流程控制。

6.1 單分支IF

語法:if 條件判斷; then  判斷體   fi

# 案例:判斷一個小姐姐的年紀是否是18。
[root@dxd ~]# ./first.sh 
是18歲
[root@dxd ~]# cat first.sh 
#!/bin/bash
old=18

if [ $old -eq 18 ];then
    echo "是18歲"
fi

6.2 雙分支IF

語法:if 條件判斷; then  判斷體  else 判斷體  fi

[root@dxd ~]# ./first.sh 
是18歲
[root@dxd ~]# vim first.sh 
[root@dxd ~]# ./first.sh 
不是18歲
[root@dxd ~]# cat first.sh 
#!/bin/bash
old=19

if [ $old -eq 18 ];then
    echo "是18歲"
else
    echo "不是18歲"
fi

6.3 多分支IF

語法:if 條件判斷; then  判斷體 elif 判斷體  else 判斷體  fi

[root@dxd ~]# cat first.sh
#!/bin/bash

read -p "請輸入你的性別:" sex

if [ "$sex" == "男" ];then

    echo "性別:男"
elif [ "$sex" == "女" ];then

    echo "性別:女"

else
    echo "保密"
fi
[root@dxd ~]# ./first.sh 
請輸入你的性別:男
性別:男
[root@dxd ~]# ./first.sh 
請輸入你的性別:
保密
[root@dxd ~]# 

6.4 案例

# 2.傳入一個檔案路徑,判斷檔案的型別
[root@dxd ~]# ./first1.sh 
請輸入檔案路徑:、^H、
不是一個路徑
[root@dxd ~]# ./first1.sh 
請輸入檔案路徑:/root
是一個路徑
[root@dxd ~]# cat first1.sh 
#!/bin/bash

read -p "請輸入檔案路徑:" paths

if [ -d $paths ];then
        echo "是一個路徑"
else
        echo "不是一個路徑"
fi



# 2.判斷80埠是否正在被使用
[root@dxd ~]# cat first1.sh 
#!/bin/bash

netstat -nutlp | grep -w '\b80\b' &>/dev/null

if [ $? -eq 0 ];then
   echo "80埠正在被使用"
else
   echo "80埠未被使用"
fi
[root@dxd ~]# ./first1.sh 
80埠正在被使用

7、流程控制之case語句

語法:

case [變數] in
判斷1)
	判斷之後需要執行的內容
	;;
判斷2)
	判斷之後需要執行的內容
	;;
判斷3)
	判斷之後需要執行的內容
	;;
*)
	其他需要執行的內容
esac

7.1案例

# 案例1:判斷一個使用者是否是超級使用者
[root@dxd ~]# ./first.sh 
請輸入使用者名稱:default
普通使用者
[root@dxd ~]# ./first.sh
請輸入使用者名稱:test
未知使用者
[root@dxd ~]# cat first.sh 
#!/bin/bash

read -p '請輸入使用者名稱:' users

case $users in
root)
        echo "超級管理員"
        ;;
default)
        echo "普通使用者"
        ;;
*)
        echo "未知使用者"
esac


# 案例2:編寫nginx啟動指令碼

#!/bin/bash

# 需要實現Nginx的啟動、重啟、關閉等功能

read -p '請輸入:' status

. /etc/init.d/functions

case $status in
start)

	netstat -nutlp | grep 'nginx: master' &>/dev/null
	
	if [ $? -ne 0 ];then
		/usr/sbin/nginx -c /etc/nginx/nginx.conf
		if [ $? -eq 0 ];then
			action "Nginx啟動成功" /bin/true
		else
			action "Nginx啟動失敗" /bin/false
		fi
	else
		action 'Nginx程序已經存在' /bin/true
	fi
	;;
stop)
	/usr/sbin/nginx -c /etc/nginx/nginx.conf -s stop 
	if [ $? -eq 0 ];then
		action "Nginx關閉成功" /bin/true
	else
		action "Nginx關閉失敗" /bin/false
	fi
	;;
restart)
 	/usr/sbin/nginx -c /etc/nginx/nginx.conf -s reopen
	if [ $? -eq 0 ];then
		action "Nginx重啟成功" /bin/true
	else
		action "Nginx重啟失敗" /bin/false
	fi
	;;
*)
	echo "請選擇:start、stop或者restart"
ecas

知識儲備:
	action命令是用來顯示執行結果格式的命令,預設系統不支援`action`,需要執行`. /etc/init.d/functions`命令才行

8、流程控制之while語句

8.1 while迴圈控制語句

語法:
while 條件
do
	迴圈體
done

8.2 until迴圈控制語句

語法:
until 條件
do
	迴圈體
done

8.3 while與until語句的區別

while 條件為true時迴圈;until 條件為false時迴圈。

8.5 迴圈中的break和continue

關鍵字 解釋
break 跳出本層迴圈
continue 跳出本次迴圈

8.4 案例

# 案例:猜年齡

#!/bin/bash

old=18

while true
do
	read -p '請輸入年齡:' num

	if [ "$old" ==  "$num" ];then
			echo "猜對了"
			break
	elif [ $old -gt $num ];then
			echo "猜小了"
	else 
			echo "猜大了"
	fi
done


# 案例:測試break和continue

#!/bin/bash

while true
do
	echo "第一層開始"

	while true
	do
		sleep 1
		echo "第二層開始"
		break
		echo "第二層結束"
	done
	
	echo "第一層結束"
	sleep 1
done



# 案例:監控伺服器

#!/bin/bash

fails=0

while [ $fails -lt 3 ]
do
        ping -c 1 -t 1 172.17.16.20 &>/bin/null
        if [ $? -ne 0 ];then
                let fails++
        fi
done

echo "探測失敗 $fails 次"

[root@dxd ~]# ./first.sh 
探測失敗 3 次

9、流程控制之for迴圈

語法:

# shell風格的語法
for 變數 in [取值列表]
do
	迴圈體
done

# C語言風格的語法
for (( 初始值; 條件; 步長))
do
	迴圈體
done

9.1 案例

# 案例:輸出10個數
#!/bin/bash

for i in {1..10}
do
	echo $i
done

for (( i=1; i<=10; i++))
do
	echo $i
done

# 案例:探測當前網段有哪些IP可用

#!/bin/bash
for i in {1..254}
do
	ping -c 1 -W 1 172.17.16.$i &>/dev/null
	if [ $? -eq 0 ];then
		echo "172.17.16.$i 被佔用"
	else
		echo "172.17.16.$i 可用"
	fi
done


# 案例:測試/root目錄中各種型別的檔案的個數
#!/bin/bash

file=0
dir=0
all=0

for i in `ls /root`
do
	if [ -f /root/$i ];then
		let file++
	elif [ -d /root/$i ];then
		let dir++
	else
		let all++
	fi
done

echo " 普通檔案有 $file 個;普通資料夾有 $dir 個; 其他型別檔案有 $all 個"

10、流程控制之select語句

select表示式是bash的一種擴充套件應用,擅長於互動式場合。使用者可以從一組不同的值中進行選擇。

語法:
select 變數 in [值列表]
do
	迴圈體
done

10.1 案例

# 案例:選擇你的愛好
#!/bin/bash

PS3='請選擇你的愛好:'

select i in '吃' '睡' '玩'
do
        echo "你選擇的是 $i "
done



# 案例:編寫一個Nginx啟動指令碼,需要實現Nginx的啟動、重啟、關閉等功能
#!/bin/bash

. /etc/init.d/functions

PS3="請選擇操作Nginx的狀態:"

select status in start stop restart quit
do

	if [ "$status" == "quit" ];then
		break
	fi

	case $status in
	start)

		netstat -nutlp | grep 'nginx: master' &>/dev/null

		if [ $? -ne 0 ];then
			/usr/sbin/nginx -c /etc/nginx/nginx.conf
			if [ $? -eq 0 ];then
				action "Nginx啟動成功" /bin/true
			else
				action "Nginx啟動失敗" /bin/false
			fi
		else
			action 'Nginx程序已經存在' /bin/true
		fi
		;;
	stop)
		/usr/sbin/nginx -c /etc/nginx/nginx.conf -s stop 
		if [ $? -eq 0 ];then
			action "Nginx關閉成功" /bin/true
		else
			action "Nginx關閉失敗" /bin/false
		fi
		;;
	restart)
		/usr/sbin/nginx -c /etc/nginx/nginx.conf -s reopen
		if [ $? -eq 0 ];then
			action "Nginx重啟成功" /bin/true
		else
			action "Nginx重啟失敗" /bin/false
		fi
		;;
	*
		echo "請選擇:start、stop或者restart"
	esac

done

11、函式

# 什麼是函式?
函式就是用來盛放一組程式碼的容器,函式內的一組程式碼一定具備某種特定的功能,稱之為一組程式碼塊。
呼叫函式便可以觸發函式內的程式碼塊。這個過程可以實現程式碼的複用。

# 為什麼要用函式?
1、為了減少程式碼冗餘
2、提升程式碼的組織結構性、可讀性
3、增強拓展性

# 函式必須遵守的原則?
先定義,後呼叫。

11.1 函式的語法

# 語法一:推薦使用
function [函式名稱] () {
	函式體
}

# 語法二:
function 函式名 {
	函式體
}

# 語法三:
函式名 {
	函式體
}

11.2 案例

# 案例:計算1+1=2
#!/bin/bash

function add() {
	let n=1+1
	echo $n
}

function add1 {
	let n=1+1
	echo $n
}

add2 {
	let n=1+1
	echo $n
}

11.3 呼叫函式

11.3.1 有引數函式

# 直接在函式名之後傳入即可。

[root@dxd ~]# ./first.sh
2
[root@dxd ~]# vim first.sh
#!/bin/bash

function add() {
    let n=$1+$2
    echo $n
}

add 1 2

11.3.2 無引數函式

[root@dxd ~]# ./first.sh
2
[root@dxd ~]# vim ./first.sh
#!/bin/bash

function add() {
	let n=1+1
	echo $n
}

add

11.4 案例

# 案例:實現一個簡單的計算器
#!/bin/bash

function j() {
	let n=$1+$2
	echo $n
}

function ja() {
	let n=$1-$2
	echo $n
}

function c() {
	let n=$1*$2
	echo $n
}

function cu() {
	let n=$1/$2
	echo $n
}

PS3="請選擇計算模式:"

select i in + - x /
do
	case $i in
	+)
		j $1 $2
		break
		;;
	-)
		ja $1 $2
		break
		;;
	x)
		c $1 $2
		break
		;;
	/)
		cu $1 $2
		break
		;;
	*)
		echo "error"
	esac
done


# 案例:實現Nginx指令碼
#!/bin/bash

. /etc/init.d/functions

function start() {

    netstat -nutlp | grep 'nginx: master' &>/dev/null

    if [ $? -eq 0 ];then
	action "Nginx程序已存在" /bin/true
    else
        /usr/sbin/nginx -c /etc/nginx/nginx.conf
        if [ $? -eq 0 ];then
	    action "Nginx啟動成功" /bin/true
	else
	    action "Nginx啟動失敗" /bin/false
	fi
    fi

}

function stop() {
    netstat -nutlp | grep 'nginx: master' &>/dev/null

    if [ $? -eq 0 ];then
	/usr/sbin/nginx -c /etc/nginx/nginx.conf -s stop
	if [ $? -eq 0 ];then
		action "Nginx關閉成功" /bin/true
	else
		action "Nginx關閉失敗" /bin/false
	fi
    else
        action "Nginx未啟動" /bin/false
    fi
}

function restart() {
    netstat -nutlp | grep 'nginx: master' &>/dev/null
    if [ $? -eq 0 ];then
        stop
	start
    else
	start
    fi
}

PS3="請選擇Nginx操作模式:"

select i in start stop restart quit
do
	case $i in
	start)
		start
		;;
	stop)
		stop
		;;
	restart)
		restart
		;;
	quit)
		break 2
		;;
	*)
		echo "請選擇:start、stop或者restart"
	esac

done

11.5 返回值(return)

# 函式中的返回值一般是[0 ~ 128]

#!/bin/bash

. /etc/init.d/functions

function start() {

    netstat -nutlp | grep 'nginx: master' &>/dev/null

    if [ $? -eq 0 ];then
		action "Nginx程序已存在" /bin/true
    else
        /usr/sbin/nginx -c /etc/nginx/nginx.conf
        if [ $? -eq 0 ];then
	    	action "Nginx啟動成功" /bin/true
		else
			action "Nginx啟動失敗" /bin/false
			return 1
		fi
    fi

    return 0
}

function stop() {
    netstat -nutlp | grep 'nginx: master' &>/dev/null

    if [ $? -eq 0 ];then
		/usr/sbin/nginx -c /etc/nginx/nginx.conf -s stop
	if [ $? -eq 0 ];then
		action "Nginx關閉成功" /bin/true
	else
		action "Nginx關閉失敗" /bin/false
		return 1
	fi
    else
        action "Nginx未啟動" /bin/false
    fi
    return 0
}

function restart() {
    netstat -nutlp | grep 'nginx: master' &>/dev/null
    if [ $? -eq 0 ];then
        stop && start
		[ $? -eq 0 ] && return 0 || return 1
    else
		start
		[ $? -eq 0 ] && return 0 || return 1
    fi
}

PS3="請選擇Nginx操作模式:"

select i in start stop restart quit
do
	case $i in
	start)
		start
		;;
	stop)
		stop
		;;
	restart)
		restart
		;;
	quit)
		break 2
		;;
	*)
		echo "請選擇:start、stop或者restart"
	esac

done

12、陣列

# 陣列分類
1、普通陣列
2、關聯陣列

# 為什麼要用陣列?
將具有相同屬性的一組元素集合到一起,避免單獨定義麻煩。

12.1 陣列的定義

# 普通陣列的定義方式
declare -a array=(1 2 3 4 5 6 7 8 9)

# 關聯陣列的定義方式
declare -A array1=([name]=shanhe)

12.2 陣列的訪問

普通陣列:正向索引從0開始,反向索引從-1開始。
關聯陣列:按照下標。

12.4 遍歷陣列

# 知識儲備:
  取所有值:${array[*]}
  取所有鍵:${!array1[*]}

12.4.1 遍歷普通陣列

#!/bin/bash

declare -a array=(1 2 3 4 5 6 7 8 9)

for i in ${array[*]}
do
	echo $i
done

12.4.2 遍歷關聯陣列

[root@dxd ~]# for i in ${!array1[*]}; 
do 
	echo ${array1[$i]}; 
done

[root@dxd ~]# for i in ${array1[*]}
do
	echo $i
done

12.4.3 案例

# 案例:通過陣列判斷當前資料夾中的各種檔案型別的個數
[root@dxd ~]# cat first2.sh
#!/bin/bash

declare -a array=(`ls`)

dir=0
file=0

for i in ${array[*]}
do
    [ -d $i ] && let dir++
    [ -f $i ] && let file++
done

echo "普通檔案有:$file 個,普通資料夾有:$dir 個。"

13、訊號處理

13.1 HUP(1)訊號

13.1.1 掛起

[root@dxd ~]# cat first3.sh
#!/bin/bash

echo $$

sleep 1000

[root@dxd ~]# kill -1 1993

13.1.2 重新載入配置

[root@dxd ~]# kill -s HUP 2277
等價
[root@dxd ~]# kill -1 2277

13.2 退出訊號

- INT(2):由ctrl + c 發出的訊號。
- QUIT(3):由 ctrl + \ 發出的訊號。
- TSTP(20):由 ctrl + z 發出的訊號。

# 知識儲備:trap

#!/bin/bash

# 捕捉 ctrl + c 訊號
trap "echo 已經捕捉到 ctrl + c訊號"

sleep 1000

13.3 終止訊號

- KILL(9):殺死訊號
  立即回收記憶體。

- TERM(15):停止訊號
  先通知程序終止,超時未終止的訊號再殺死。

14、Systemctl管理指令碼

systemctl配置檔案路徑:/usr/lib/systemd/system

14.1 systemctl包含的檔案

*.service:操作服務檔案
*.target:開機級別的服務檔案

14.2 systemctl配置的格式

 unit:主要是用來做服務說明的(沒有實際意義)。
 service:核心區域(各種服務操作【開啟、停止、重啟等】)
 install:用來配置多使用者

14.3 unit

Description:主要是用來簡介。
After:表示該服務需要在那些服務後啟動。
Before:表示在某些服務之前啟動。

14.4 Service

- Type:表示在後臺執行的模式
- User:表示服務執行的使用者,預設:當前使用者
- Group:表示執行的使用者組,預設:當前使用者
- PIDFile:指定PID檔案路徑
- ExecStart:指定服務的啟動命令
- ExecReload:指定服務重啟時執行的命令
- ExecStop:指定服務停止執行的命令,預設執行 kill -s TERM
- PrivateTmp:指定是否分配臨時的獨立空間
- Environment:指定環境變數的
- ExecStartPre:啟動服務之前執行的命令
- ExecStartPost:服務啟動之後執行的命令
- ExecStopPost:服務停止之後執行的命令

14.5 Install

# WantedBy=multi-user.target # 定支援多使用者模式
[root@dxd system]# cat nginx2.service
[Unit]
Description=Nginx Service 測試服務檔案
After=network.target
Before=mysqld.service

[Service]
Type=foking
User=root
Group=root
PIDFile=/run/nginx2.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStopPost=echo "Stop Success"
ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf
ExecReload=/usr/bin/kill -s HUP $MAINPID
ExecStop=/usr/bin/kill -s TERM $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

14.6 Type型別

- forking:需要被啟動的程序擁有主程序和子程序。
- simple:啟動命令產生程序就是主程序
- notify:類似於simple

# 通常情況下,在後臺執行的命令使用forking;在前臺執行的命令使用simple和notify等。

14.7 重啟型別(Restart)

- on:一旦程式出現宕機,system不做任何管理 (預設)
- on-success:只有正常退出情況下,才會重啟。
- on-failure:非正常退出時,重啟
- on-abnaomal:只有訊號終止或超時,才會重啟
- always:不管什麼原因退出,都會重啟

# 通常情況下,採用on或者on-success兩個引數。

14.8 案例:啟動Nginx

[root@dxd ~]# vim nginx.sh
#!/bin/bash

. /etc/init.d/functions

args=$1

function print() {
    [ $? -eq 0 ] && action "Nginx $args is " /bin/true || action "Nginx $args is " /bin/false
}

case $args in
start)
    netstat -nutlp | grep 'nginx: master' &>/dev/null

    if [ $? -ne 0 ]; then
        /usr/sbin/nginx -c /etc/nginx/nginx.conf
    fi
    print
    ;;
stop)
    netstat -nutlp | grep 'nginx: master' &>/dev/null

    if [ $? -eq 0 ]; then
        /usr/sbin/nginx -c /etc/nginx/nginx.conf -s stop
    fi
    print
    ;;
restart)
    netstat -nutlp | grep 'nginx: master' &>/dev/null

    if [ $? -eq 0 ]; then
        /usr/sbin/nginx -c /etc/nginx/nginx.conf -s stop
        sleep 1
    fi

    /usr/sbin/nginx -c /etc/nginx/nginx.conf
    print
    ;;

test)
    /usr/sbin/nginx -c /etc/nginx/nginx.conf -t
    print
    ;;

*)
    echo "Please Enter start/stop/test/restart"
    ;;
esac


# Service
[root@dxd ~]# vim /usr/lib/systemd/system/nginx3.service
[Unit]
Description=Nginx Service 指令碼服務檔案

[Service]
Type=forking
ExecStartPre=/usr/local/bin/nginx.sh test
ExecStart=/usr/local/bin/nginx.sh start
ExecReload=/usr/local/bin/nginx.sh restart
ExecStop=/usr/local/bin/nginx.sh stop
PrivateTmp=true

[Install]
WantedBy=multi-user.target