一文學完Linux Shell程式設計,比書都好懂

2022-05-27 06:01:56

一、 Shell 程式設計

1. 簡介

Shell 是一個用 C 語言編寫的程式,通過 Shell 使用者可以存取作業系統核心服務。

Shell 既是一種命令語言,又是一種程式設計語言。

Shell script 是一種為 shell 編寫的指令碼程式。Shell 程式設計一般指 shell 指令碼程式設計,不是指開發 shell 自身。

Shell 程式設計跟 java、php 程式設計一樣,只要有一個能編寫程式碼的文字編輯器和一個能解釋執行的指令碼直譯器就可以了。

Linux 的 Shell 直譯器 種類眾多,一個系統可以存在多個 shell,可以通過 cat /etc/shells 命令檢視系統中安裝的 shell 直譯器。

Bash 由於易用和免費,在日常工作中被廣泛使用。同時,Bash 也是大多數 Linux 系統預設的 Shell。

shell 直譯器

java 需要虛擬機器器直譯器, 同理 shell 指令碼也需要解析器,如下所示:

[root@node01 shells] cat /etc/shells
/bin/sh
/bin/bash
/sbin/nologin
/bin/dash
/bin/tcsh
/bin/csh

2. 快速入門

1) 編寫指令碼

新建 /export/hello.sh 檔案,內容如下:

#!/bin/bash

echo 'hello world'

#!是一個約定的標記,它告訴系統這個指令碼需要什麼直譯器來執行,即使用哪一種 Shell。

echo 命令用於向視窗輸出文字。

2) 執行 shell 指令碼

執行方式一
[root@node01 shells] /bin/sh 01.sh
hello world

[root@node01 shells] /bin/bash 01.sh
hello world

問題:bash 和 sh 是什麼關係?

答:sh 是 bash 的 快捷方式

執行方式二

方式一的簡化方式:

[root@node01 shells] bash hello.sh
hello world

[root@node01 shells] sh hello.sh
hello world

問題:為什麼可以省略 /bin/

答:因為 PATH 環境變數中增加了 /bin/目錄, 所以 使用/bin/sh 等類似指令時, 可以省略 /bin

執行方式三

./檔名

[root@node01 shells] ./hello.sh
-bash: ./01.sh: 許可權不夠

問題:許可權不夠怎麼辦?

[root@node01 shells] chmod 755 hello.sh

# 再次執行:
[root@node01 shells] ./hello.sh
hello world!

3. shell 變數

1) 簡介

在 shell 指令碼中, 定義變數時,變數名不加美元符號$,如:

your_name="runoob.com"

注意 : 變數名和等號之間不能有空格,這可能和你熟悉的所有程式語言都不一樣。

同時,變數名的命名須遵循如下規則:

  • 命名只能使用英文字母,數位和下劃線,首個字元不能以數位開頭。
  • 中間不能有空格,可以使用下劃線_
  • 不能使用標點符號。
  • 不能使用 bash 裡的關鍵字(可用 help 命令檢視保留關鍵字)。

有效的 Shell 變數名範例如下:

RUNOOB
LD_LIBRARY_PATH
_var
var2

無效的變數命名:

?var=123
user*name=runoob

除了顯式地直接賦值,還可以用語句給變數賦值,如:

for file in `ls /etc`

for file in $(ls /etc)

以上語句將 /etc 下目錄的檔名迴圈出來。

2) 使用變數

使用一個定義過的變數,只要在變數名前面加美元符號即可,如:

your_name="zhangsan"

echo $your_name

echo ${your_name}

變數名外面的花括號是可選的,加不加都行,加花括號是為了幫助直譯器識別變數的邊界,比如下面這種情況:

for skill in java php python; do
    echo "I am good at ${skill}Script"
done

如果不給 skill 變數加花括號,寫成echo "I am good at $skillScript",直譯器就會把$skillScript當成一個變數(其值為空),程式碼執行結果就不是我們期望的樣子了。

推薦給所有變數加上花括號,這是個好的程式設計習慣。

已定義的變數,可以被重新定義,如:

your_name="tom"
echo $your_name
your_name="alibaba"
echo $your_name

這樣寫是合法的,但注意,第二次賦值的時候不能寫$your_name="alibaba",使用變數的時候才加美元符。

3) 刪除變數

使用 unset 命令可以刪除變數。語法:

unset variable_name

變數被刪除後不能再次使用。unset 命令不能刪除唯讀變數。

範例

#!/bin/sh
myUrl="http://www.runoob.com"
unset myUrl
echo $myUrl

以上範例執行將沒有任何輸出。

4) 唯讀變數

使用 readonly 命令可以將變數定義為唯讀變數,唯讀變數的值不能被改變。

下面的例子嘗試更改唯讀變數,結果報錯:

#!/bin/bash

myUrl="http://www.google.com"
readonly myUrl
myUrl="http://www.runoob.com"

執行指令碼,結果如下:

/bin/sh: NAME: This variable is read only.

4. 字串

字串是 shell 程式設計中最常用最有用的資料型別(除了數位和字串,也沒啥其它型別好用了),字串可以用單引號,也可以用雙引號,也可以不用引號。

1) 單引號

skill='java'

str='I am goot at $skill'

echo $str

輸出結果為:

I am goot at $skill

單引號字串的限制:

  • 單引號裡的任何字元都會原樣輸出,單引號字串中的變數是無效的;
  • 單引號字串中不能出現單獨一個的單引號(對單引號使用跳脫符後也不行),但可成對出現,作為字串拼接使用。

2) 雙引號

skill='java'

str="I am goot at $skill"

echo $str

輸出結果為:

I am goot at java

雙引號的優點:

  • 雙引號裡可以有變數
  • 雙引號裡可以出現跳脫字元

3) 獲取字串長度

skill='java'

echo ${skill}    # 輸出結果: java

echo ${#skill}   # 輸出結果: 4

或者:  expr length "iamlilei"   #輸出結果:  8

4) 提取子字串

substring(2)

substring(2,3)

以下範例從字串第 2 個字元開始擷取 4 個字元:

str="I am goot at $skill"

echo ${str:2} 	  # 輸出結果為: am goot at java  從第二個字元開始擷取,到結尾

echo ${str:2:2}    # 輸出結果為: am  從第二個字元開始擷取,擷取2個字元

5) 查詢子字串

查詢字元 am 的位置(哪個字母先出現就計算哪個):

str="I am goot at  $skill"
echo `expr index "$str" am`    # 輸出是: 3

或者:
expr index "iamlilei" am      #輸出結果: 2   返回在STRING中找到CHARS字串的位置;否則,返回0

注意: 以上指令碼中 ` 是反引號(Esc 下面的),而不是單引號 ',不要看錯了哦。

5. 傳遞引數

我們可以在執行 Shell 指令碼時,向指令碼傳遞引數,指令碼內獲取引數的格式為:$n

n 代表一個數位,1 為執行指令碼的第一個引數,2 為執行指令碼的第二個引數,以此類推……

範例

以下範例我們向指令碼傳遞三個引數,並分別輸出,其中 $0 為執行的檔名:

vim /export/sh/param.sh

#!/bin/bash

echo "Shell 傳遞引數範例!";

echo "執行的檔名:$0";

echo "第一個引數為:$1";

echo "第二個引數為:$2";

echo "第三個引數為:$3";

為指令碼設定可執行許可權,並執行指令碼,輸出結果如下所示:

$ chmod 755 param.sh

$ ./param.sh 1 2 3

Shell 傳遞引數範例!

執行的檔名:./param.sh

第一個引數為:1

第二個引數為:2

第三個引數為:3

另外,還有幾個特殊字元用來處理引數:

引數處理 說明
$# 傳遞到指令碼的引數個數
$* 以一個單字串顯示所有向指令碼傳遞的引數。 如"$*"用「"」括起來的情況、以"$1 $2 … $n"的形式輸出所有引數。
$$ 指令碼執行的當前程序 ID 號
$! 後臺執行的最後一個程序的 ID 號
$@ $*相同,但是使用時加引號,並在引號中返回每個引數。 如"$@"用「"」括起來的情況、以"$1" "$2" … "$n" 的形式輸出所有引數。
$- 顯示 Shell 使用的當前選項,與 set 命令功能相同。
$? 顯示最後命令的退出狀態。0 表示沒有錯誤,其他任何值表明有錯誤。
#!/bin/bash

echo "Shell 傳遞引數範例!";

echo "第一個引數為:$1";

echo "引數個數為:$#";

echo "傳遞的引數作為一個字串顯示:$*";

執行指令碼,輸出結果如下所示:

$ chmod +x test.sh

$ ./test.sh 1 2 3

Shell 傳遞引數範例!

第一個引數為:1

引數個數為:3

傳遞的引數作為一個字串顯示:1 2 3

$*$@ 區別:

  • 相同點:都是參照所有引數。
  • 不同點:只有在雙引號中體現出來。假設在指令碼執行時寫了三個引數 1、2、3,,則 " * " 等價於 "1 2 3"(傳遞了一個引數),而 "@" 等價於 "1" "2" "3"(傳遞了三個引數)。
#!/bin/bash

echo "-- $* 演示 ---"
for i in "$*"; do
    echo $i
done

echo "-- $@ 演示 ---"
for i in "$@"; do
    echo $i
done

執行指令碼,輸出結果如下所示:

$ chmod +x test.sh

$ ./test.sh 1 2 3

-- $* 演示 ---
1 2 3

-- $@ 演示 ---
1
2
3

6. Shell 算術運運算元

1) 簡介

Shell 和其他程式設計一樣,支援包括:算術、關係、布林、字串等運運算元。

原生 bash 不支援簡單的數學運算,但是可以通過其他命令來實現,例如 expr。

expr 是一款表示式計算工具,使用它能完成表示式的求值操作。

例如,兩個數相加:

val=`expr 2 + 2`
echo $val

注意:

表示式和運運算元之間要有空格,例如 2+2 是不對的,必須寫成 2 + 2

完整的表示式要被 ` 包含,注意不是單引號,在 Esc 鍵下邊。

下表列出了常用的算術運運算元,假定變數 a 為 10,變數 b 為 20:

運運算元 說明 舉例
+ 加法 expr $a + $b 結果為 30。
- 減法 expr $a - $b 結果為 -10。
* 乘法 expr $a * $b 結果為 200。
/ 除法 expr $b / $a 結果為 2。
% 取餘 expr $b % $a 結果為 0。
= 賦值 a=$b 將把變數 b 的值賦給 a。
== 相等。用於比較兩個數位,相同則返回 true。 [ $a == $b ] 返回 false。
!= 不相等。用於比較兩個數位,不相同則返回 true。 [ $a != $b ] 返回 true。

注意:條件表示式要放在方括號之間,並且要有空格,例如: [$a==$b] 是錯誤的,必須寫成 [ $a == $b ]

2) 例子

#!/bin/bash

a=4

b=20

#加法運算

each expr $a + $b

#減法運算

echo expr $a - $b

#乘法運算,注意*號前面需要反斜槓

echo expr $a \* $b

#除法運算

echo $a / $b



此外,還可以通過(())、$(())、$[]進行算術運算。



 ((a++))

echo "a = $a"

c=$((a + b))

d=$[a + b]

echo "c = $c"

echo "d = $d"

7. 流程控制

1) if else

1.1 if

if 語句語法格式:

if condition; then
    command1
    command2
    ...
    commandN
fi

demo

[root@hadoop01 export]# cat if_test.sh
#!/bin/bash

a=20

if [ $a -gt 10 ]; then
        echo "a 大於 10"
fi

末尾的 fi 就是 if 倒過來拼寫,後面還會遇到類似的。

1.2 if else

if else 語法格式:

if condition; then
    command1
    command2
    ...
    commandN
else
    command
fi

1.3 if else-if else

if else-if else 語法格式:

if condition1; then
    command1
elif condition2; then
    command2
else
    commandN
fi

以下範例判斷兩個變數是否相等:

關係運算子

關係運算子只支援數位,不支援字串,除非字串的值是數位。

下表列出了常用的關係運算子,假定變數 a 為 10,變數 b 為 20:

運運算元 說明 英文 舉例
-eq 檢測兩個數是否相等,相等返回 true。 equal [ $a -eq $b ] 返回 false。
-ne 檢測兩個數是否不相等,不相等返回 true。 not equal [ $a -ne $b ] 返回 true。
-gt 檢測左邊的數是否大於右邊的,如果是,則返回 true。 greater than [ $a -gt $b ] 返回 false。
-lt 檢測左邊的數是否小於右邊的,如果是,則返回 true。 less than [ $a -lt $b ] 返回 true。
-ge 檢測左邊的數是否大於等於右邊的,如果是,則返回 true。 Greater than or equal to [ $a -ge $b ] 返回 false。
-le 檢測左邊的數是否小於等於右邊的,如果是,則返回 true。 Less than or equal to [ $a -le $b ] 返回 true。

案例:

[root@hadoop01 export]# cat if_test.sh
#!/bin/bash

a=20
b=10

# 需求1: 判斷 a 是否 100
if [ $a > 100 ]; then
        echo "$a 大於 100"
fi


# 需求2: 判斷 a 是否等於 b
if [ $a -eq $b ]; then
        echo "$a 等於 $b"
else
        echo "$a 不等於 $b"
fi

# 需求3: 判斷 a 與 b 比較
if [ $a -lt $b ]; then
        echo "$a 小於 $b"
elif [ $a -eq $b ]; then
        echo "$a 等於 $b"
else
        echo "$a 大於 $b"
fi


# 需求4: 判斷 (a + 10) 和 (b * b) 比較大小
if test $[ a + 10 ] -gt $[ b * b ]; then
        echo "(a+10) 大於 (b * b)"
else
        echo "(a+10) 小於或等於 (b*b)"
fi

2) for 迴圈

參考連結:Linux Shell程式設計pdf版本