1. 程式人生 > 其它 >一文詳解shell程式設計(shell程式設計筆記)

一文詳解shell程式設計(shell程式設計筆記)

本文為快速入門教程,如果你所在的行業需要用到很深入的shell指令碼知識,可參考書籍《Linux命令列與shell指令碼程式設計大全》,豆瓣連結:https://book.douban.com/subject/26854226/

圖片名稱

1. 簡介

常見的程式語言分為兩類:一個是編譯型語言,它們執行前全部一起要經過編譯器的編譯。另一個解釋型語言,執行時,需要使用直譯器一行一行地轉換為程式碼,相關的語言分類如下:
在這裡插入圖片描述

真正能夠控制計算機硬體(CPU、記憶體、顯示器等)的只有作業系統核心(Kernel),圖形介面和命令列只是架設在使用者和核心之間的一座橋樑
在這裡插入圖片描述
在Linux下,這個命令列程式叫做Shell。由此可見,Shell 是將核心、程式和使用者連線了起來。常見的 Shell 有 sh、bash、csh、tcsh、ash 等。

  • sh:sh 的全稱是 Bourne shell,由 AT&T 公司的 Steve Bourne開發,為了紀念他,就用他的名字命名了。sh 是 UNIX 上的標準 shell,很多 UNIX 版本都配有 sh。sh 是第一個流行的 Shell。
  • bash:全稱Bourne Again shell,bash shell 是 Linux 的預設 shell。
  • csh:sh 之後另一個廣為流傳的 shell 是由柏克萊大學的 Bill Joy 設計的,這個 shell 的語法有點類似C語言,所以才得名為 C shell ,簡稱為 csh。
  • tcsh:tcsh 是 csh 的增強版,加入了命令補全功能,提供了更加強大的語法支援。
  • ash:一個簡單的輕量級的 Shell,佔用資源少,適合運行於低記憶體環境。

可以通過命令 cat /etc/shells命令來檢視自己電腦的相容的shell版本,另外,還可以通過命令echo $SHELL來檢視自己電腦預設的SHELL環境。

$ cat  /etc/shells
/bin/sh
/bin/bash
/bin/rbash
/bin/dash
$ echo $SHELL
/bin/bash

2. 執行shell指令碼的幾種方式

在你的工作空間新建一個文字test.sh(副檔名並不影響指令碼執行,副檔名.sh不寫,或者隨便取也可以,只不過約定俗成用.sh),然後輸入以下內容:

#!/bin/bash
echo "Hello World !"

第一行為註釋,表示使用bash的shell,不寫也無所謂。在終端輸入以下命令都可以執行該指令碼:

./test.sh #方式1
source test.sh # 方式2
sh test.sh # 方式3
bash test.sh # 方式4
/bin/sh test.sh #方式5
/bin/bash test.sh # 方式6

3. 註釋

  • 單行註釋
#--------------------------------------------
# 這是一個註釋
# 這是一個註釋
# 這是一個註釋
# 這是一個註釋
#--------------------------------------------
  • 多行註釋
:<<EOF
註釋內容...
註釋內容...
註釋內容...
EOF

其中EOF可以用其他符號代替

4. 變數

變數的命名規則與C/C++等類似,它的規則如下:

  • 命名只能使用英文字母,數字和下劃線,首個字元不能以數字開頭
  • 不能使用bash裡的關鍵字(可用終端輸入help命令檢視保留關鍵字)。

4.1 系統變數

系統變數也稱為環境變數,所有的程式都可以訪問環境變數,一般用大寫字母表示,例如$HOME、$PWD、$SHELL、$USER等。例如:

$ echo $HOME
/home/myComputer

4.2 區域性變數

區域性變數也稱為自定義變數,區域性變數在指令碼或命令中定義,僅在當前shell例項中有效,其他shell啟動的程式不能訪問區域性變數。

1)定義和使用變數

$ my_name="QLee" #定義變數,=號左右兩側不能有空格
$ echo $my_name #變數的使用
$ echo ${my_name} #變數的使用
QLee
QLee

變數外的括號,是可選的,加花括號是為了幫助直譯器識別變數的邊界。

2)只讀變數

#!/bin/bash
my_name="QLee"
readonly my_name #指定只讀變數,不用$
my_name="lisa"

輸出:

test.sh: 行 4: my_name: 只讀變數

3)刪除變數

#!/bin/bash
my_name="QLee"
unset my_name #刪除變數,不用$
echo $my_name

以上echo不會有輸出,因為my_name被刪除。另外,注意不能刪除只讀變數

4)字串

shell只有數字和字串的資料型別,所以這裡介紹一下字串型別。

  • 單引號形式
str='this is a string'

單引號裡的任何字元都會原樣輸出,單引號字串中的變數是無效的。單引號只能成對的出現,即使使用轉義符,也不能單獨出現。

  • 雙引號形式
my_name='QLee'
str="Hello,my name is \"$my_name\"! " #拼接
echo $str

輸出:

Hello,my name is "QLee"!
  • 字串拼接
my_name="QLee"
# 使用雙引號拼接
greeting="hello, "$my_name" !"
greeting_1="hello, ${my_name} !"
echo $greeting  $greeting_1
# 使用單引號拼接
greeting_2='hello, '$my_name' !'
greeting_3='hello, ${my_name} !' # 原樣輸出!!!
echo $greeting_2  $greeting_3

輸出:

hello, QLee ! hello, QLee !
hello, QLee ! hello, ${my_name} !
  • 獲取字串長度
string="abcd"
echo ${#string} #輸出 4
  • 提取字串

從位置1開始,提取4個字元

string="my name is QLee"
echo ${string:1:4} # 從第1個字元開始擷取4個字元,輸出y na
  • 查詢字串

以下表示查詢n或i的第一個位置

string="my name is QLee"
echo `expr index "$string" ni`  # 輸出4

5. shell陣列

bash中只有一維陣列,它的下標從0開始,陣列的大小沒有限定,定義陣列時用空格分開,例如:

array_name=(value0 value1 value2 value3)# 陣列的定義

或者:

array_name=(
value0
value1
value2
value3
)

或者:

array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen

5.1 讀取陣列

my_array=(A B "C" D)

echo "第一個元素為: ${my_array[0]}"
echo "第二個元素為: ${my_array[1]}"
echo "第三個元素為: ${my_array[2]}"
echo "第四個元素為: ${my_array[3]}"

輸出:

第一個元素為: A
第二個元素為: B
第三個元素為: C
第四個元素為: D

5.2 獲取陣列長度

# 取得陣列元素的個數
length=${#array_name[@]} # @或*可以獲取陣列中的所有元素。
# 或者
length=${#array_name[*]}
# 取得陣列單個元素的長度
lengthn=${#array_name[n]}

6. 基本運算子

原生bash不支援簡單的數學運算,但是可以通過其他命令來實現,例如 awk 和 expr,expr 最常用。expr 是一款表示式計算工具,使用它能完成表示式的求值操作。

關於運算子需要注意以下兩點:

  • 表示式和運算子之間要有空格,例如 2+2 是不對的,必須寫成 2 + 2;
  • 完整的表示式要被 包含,注意這個字元不是常用的單引號,在 Esc 鍵下邊。

6.1 算數運算

下表列出了常用的算術運算子,假定變數 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 ]。

6.2 關係運算符

關係運算符只支援數字,不支援字串,除非字串的值是數字。下表列出了常用的關係運算符,假定變數 a 為 10,變數 b 為 20:

運算子單詞說明舉例
-eqequal相等[ $a -eq $b ] 返回 false。
-nenot equal不相等[ $a -ne $b ] 返回 true。
-gtgreat than大於[ $a -gt $b ] 返回 false。
-ltless than小於[ $a -lt $b ] 返回 true。
-gegreat than or equal大於或等於[ $a -ge $b ] 返回 false。
-leless than or equal小於等於[ $a -le $b ] 返回 true。

6.3 布林運算子

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

運算子說明舉例
!非運算[ ! false ] 返回 true。
-o或運算[ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a與運算[ $a -lt 20 -a $b -gt 100 ] 返回 false。

6.4 邏輯運算子

以下介紹 Shell 的邏輯運算子,假定變數 a 為 10,變數 b 為 20:

運算子說明舉例
&&邏輯"與"[[ $a -lt 100 && $b -gt 100 ]] 返回 false
||邏輯"或"[[ $a -lt 100 || $b -gt 100 ]] 返回 true

注意:必須兩個"[ ]"

6.5 字串運算子

下表列出了常用的字串運算子,假定變數 a 為 “abc”,變數 b 為 “efg”:

運算子說明舉例
=是否相等[ $a = $b ] 返回 false。
!=是否不相等[ $a != $b ] 返回 true。
-z是否字串長度為0[ -z $a ] 返回 false。
-n是否字元創長度不為0[ -n “$a” ] 返回 true。
$字串是否為空,不為空返回 true。[ $a ] 返回 true。

6.6 檔案測試運算子

檔案測試運算子用於檢測 Unix 檔案的各種屬性。屬性檢測描述如下:

操作符說明舉例
-b file檢測檔案是否是塊裝置檔案,如果是,則返回 true。[ -b $file ]
-c file檢測檔案是否是字元裝置檔案,如果是,則返回 true。[ -c $file ]
-d file檢測檔案是否是目錄,如果是,則返回 true。[ -d $file ]
-f file檢測檔案是否是普通檔案(既不是目錄,也不是裝置檔案),如果是,則返回 true。[ -f $file ]
-g file檢測檔案是否設定了 SGID 位,如果是,則返回 true。[ -g $file ]
-k file檢測檔案是否設定了粘著位(Sticky Bit),如果是,則返回 true。[ -k $file ]
-p file檢測檔案是否是有名管道,如果是,則返回 true。[ -p $file ]
-u file檢測檔案是否設定了 SUID 位,如果是,則返回 true。[ -u $file ]
-r file檢測檔案是否可讀,如果是,則返回 true。[ -r $file ]
-w file檢測檔案是否可寫,如果是,則返回 true。[ -w $file ]
-x file檢測檔案是否可執行,如果是,則返回 true。[ -x $file ]
-s file檢測檔案是否為空(檔案大小是否大於0),不為空返回 true。[ -s $file ]
-e file檢測檔案(包括目錄)是否存在,如果是,則返回 true。[ -e $file ]

7. echo命令

echo為顯示命令,它有下列幾種用法。

  • 1)顯示普通字串
echo "It is a test" # It is a test
  • 2)顯示轉移字元
echo "\"It is a test\"" # "It is a test"
  • 3)顯示變數
read name # 從標準輸入讀取一行給name
echo "$name It is a test" 
  • 4)顯示換行
echo -e "OK! \n" # -e 開啟轉義
echo "It is a test"

輸出:

OK!

It is a test
  • 5)不顯示換行
echo -e "OK! \c" # -e 開啟轉義 \c 不換行
echo "It is a test"

輸出:

OK! It is a test
  • 6)顯示結果定向至檔案
echo "It is a test" > myfile
  • 原樣輸出字串,不進行轉義或取變數(用單引號)
echo '$name\"' #輸出:$name\"
  • 7)顯示命令執行結果
echo `date`# `為反引號,顯示現在的時間

8. printf命令

它的使用形式:

printf  format-string  [arguments...]

說明:

  • format-string: 為格式控制字串
  • arguments: 為引數列表。

預設 printf 不會像 echo 自動新增換行符,我們可以手動新增 \n。

$ echo "Hello, Shell"
Hello, Shell
$ printf "Hello, Shell\n"
Hello, Shell

格式輸出中,%s 輸出一個字串,%d 整型輸出,%c 輸出一個字元,%f 輸出實數,以小數形式輸出。舉例說明如下:

printf "%-10s %-8s %-4s\n" name 性別 體重kg  
printf "%-10s %-8s %-4.2f\n" QLee 男 65.3
printf "%-10s %-8s %-4.2f\n" lisa 男 75.6
printf "%-10s %-8s %-4.2f\n" yoyo 女 45.39

輸出:

在這裡插入圖片描述

printf 的轉義序列

序列說明
\a警告字元,通常為ASCII的BEL字元
\b後退
\c不顯示輸出結果中任何結尾的換行字元(只在%b格式指示符控制下的引數字串中有效),而且,任何留在引數裡的字元、任何接下來的引數以及任何留在格式字串中的字元,都被忽略
\f換頁(formfeed)
\n換行
\r回車(Carriage return)
\t水平製表符
\v垂直製表符
\\一個字面上的反斜槓字元
\ddd表示1到3位數八進位制值的字元。僅在格式字串中有效
\0ddd表示1到3位的八進位制值字元

9. 流程控制

9.1 if語句

  • 情況1:if
if condition
then
    command1 
    command2
    ...
    commandN 
fi
  • 情況2:if else
if condition
then
    command1 
    command2
    ...
    commandN
else
    command
fi
  • 情況3:if else-if else
if condition1
then
    command1
elif condition2 
then 
    command2
else
    commandN
fi

9.2 for迴圈

for var in item1 item2 ... itemN
do
    command1
    command2
    ...
    commandN
done

舉例:

for loop in 1 2 3 4 5
do
    echo "The value is: $loop"
done

輸出:

The value is: 1
The value is: 2
The value is: 3
The value is: 4
The value is: 5
for str in This is a string
do
    echo $str
done

輸出:

This
is
a
string

9.3 while語句

語法格式:只要condition為true,則繼續執行

while condition
do
    command
done

無限迴圈:

while :
do
    command
done

或者:

while true
do
    command
done

或者

for (( ; ; ))

9.4 until迴圈

語法格式:直至條件為 true 時停止

until condition
do
    command
done

9.5 case … esac多分支語句

每個 case 分支用右圓括號開始,用兩個分號 ;; 表示執行結束。

基本語法:

casein
模式1)
    command1
    command2
    ...
    commandN
    ;;
模式2)
    command1
    command2
    ...
    commandN
    ;;
esac

9.6 break和continue

和c語言的break和continue語句一樣使用。

  • break:跳出整個迴圈
  • continue:跳出這一次的迴圈,繼續執行一次迴圈。

10. 函式

shell中函式的定義格式如下:

[ function ] funname [()]
{
    action;

    [return int;]
}

說明:

  • 可以帶function fun() 定義,也可以直接fun() 定義,不帶任何引數。
  • 引數返回,可以顯示加:return 返回,如果不加,將以最後一條命令執行結果,作為返回值。

10.1 無引數、無返回值的函式

demoFun(){
    echo "這是我的第一個 shell 函式!"
}
echo "-----函式開始執行-----"
demoFun
echo "-----函式執行完畢-----"

輸出:

-----函式開始執行-----
這是我的第一個 shell 函式!
-----函式執行完畢-----

10.2 無引數、有返回值的函式

funWithReturn(){
    echo "這個函式會對輸入的兩個數字進行相加運算..."
    echo "輸入第一個數字: "
    read aNum
    echo "輸入第二個數字: "
    read anotherNum
    echo "兩個數字分別為 $aNum$anotherNum !"
    return $(($aNum+$anotherNum))
}
funWithReturn
echo "輸入的兩個數字之和為 $? !"

輸出:

這個函式會對輸入的兩個數字進行相加運算...
輸入第一個數字: 
1
輸入第二個數字: 
2
兩個數字分別為 1 和 2 !
輸入的兩個數字之和為 3 !

說明:

  • 函式返回值在呼叫該函式後通過 $? 來獲得。
  • 所有函式在使用前必須定義

10.3 有引數的函式

funWithParam(){
    echo "第一個引數為 $1 !"
    echo "第二個引數為 $2 !"
    echo "第十個引數為 $10 !"
    echo "第十個引數為 ${10} !"
    echo "第十一個引數為 ${11} !"
    echo "引數總數有 $# 個!"
    echo "作為一個字串輸出所有引數 $* !"
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73

輸出:

第一個引數為 1 !
第二個引數為 2 !
第十個引數為 10 !
第十個引數為 34 !
第十一個引數為 73 !
引數總數有 11 個!
作為一個字串輸出所有引數 1 2 3 4 5 6 7 8 9 34 73 !

注意:當n>=10時,需要使用${n}來獲取引數。

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

引數處理說明
$#傳遞到指令碼或函式的引數個數
$*以一個單字串顯示所有向指令碼傳遞的引數
$$指令碼執行的當前程序ID號
$!後臺執行的最後一個程序的ID號
[email protected]與$*相同,但是使用時加引號,並在引號中返回每個引數。
$-顯示Shell使用的當前選項,與set命令功能相同。
$?顯示最後命令的退出狀態。0表示沒有錯誤,其他任何值表明有錯誤。

11. 輸入/輸出重定向

預設情況下,標準輸入和輸出的地方都是終端,但是我們可以通過命令對輸入/輸出進行重定向。

重定向命令列表如下:

命令說明
command > file將輸出重定向到 file。
command < file將輸入重定向到 file。
command >> file將輸出以追加的方式重定向到 file。
n > file將檔案描述符為 n 的檔案重定向到 file。
n >> file將檔案描述符為 n 的檔案以追加的方式重定向到 file。
n >& m將輸出檔案 m 和 n 合併。
n <& m將輸入檔案 m 和 n 合併。
<< tag將開始標記 tag 和結束標記 tag 之間的內容作為輸入。

需要注意的是檔案描述符 0 通常是標準輸入(STDIN),1 是標準輸出(STDOUT),2 是標準錯誤輸出(STDERR)。

  • Here Document

Here Document 是 Shell 中的一種特殊的重定向方式,用來將輸入重定向到一個互動式 Shell 指令碼或程式。

例如:

cat << EOF
hello, everybody
my name is QLee
EOF

列印如下:

hello, everybody
my name is QLee
  • /dev/null 檔案

/dev/null 是一個特殊的檔案,寫入到它的內容都會被丟棄;如果嘗試從該檔案讀取內容,那麼什麼也讀不到。但是 /dev/null 檔案非常有用,將命令的輸出重定向到它,會起到"禁止輸出"的效果。

如果希望執行某個命令,但又不希望在螢幕上顯示輸出結果,那麼可以將輸出重定向到 /dev/null:

$ command > /dev/null

如果希望遮蔽 stdout 和 stderr,可以這樣寫:

$ command > /dev/null 2>&1 # 0 是標準輸入(STDIN),1 是標準輸出(STDOUT),2 是標準錯誤輸出(STDERR)。

12. 檔案包含

與C語言中的#include語句類似,shell中也有自己的包含命令,它的語法格式如下:

. filename   # 注意點號(.)和檔名中間有一空格source filename

因為是解釋語言,所以檔案包含的意義,其實就是寫一條命令執行被包含的檔案。

13. 其他命令

(1)data

顯示當前時間,格式如下:

date [選項] [+時間格式] #使用(+)號開始的引數用來指定時間格式

例如:

nowTime=`date +%Y-%M-%d-%H:%M:%S`
echo $nowTime

date的格式有以下幾種:

%H 小時(以00-23來表示)。
%I 小時(以01-12來表示)。
%K 小時(以0-23來表示)。
%l 小時(以0-12來表示)。
%M 分鐘(以00-59來表示)。
%P AM或PM。
%r 時間(含時分秒,小時以12小時AM/PM來表示)。
%s 總秒數。起算時間為1970-01-01 00:00:00 UTC。
%S 秒(以本地的慣用法來表示)。
%T 時間(含時分秒,小時以24小時制來表示)。
%X 時間(以本地的慣用法來表示)。
%Z 市區。
%a 星期的縮寫。
%A 星期的完整名稱。
%b 月份英文名的縮寫。
%B 月份的完整英文名稱。
%c 日期與時間。只輸入date指令也會顯示同樣的結果。
%d 日期(以01-31來表示)。
%D 日期(含年月日)。
%j 該年中的第幾天。
%m 月份(以01-12來表示)。
%U 該年中的週數。
%w 該周的天數,0代表週日,1代表週一,異詞類推。
%x 日期(以本地的慣用法來表示)。
%y 年份(以00-99來表示)。
%Y 年份(以四位數來表示)。
%n 在顯示時,插入新的一行。
%t 在顯示時,插入tab。
MM 月份(必要)
DD 日期(必要)
hh 小時(必要)
mm 分鐘(必要)
ss 秒(選擇性)

(2)後臺執行指令碼

test.sh &

對於基礎應用,以上知識已經足夠了。由於本人所在行業原因,暫時還不需要那麼高深的shell知識,如果後續有深入研究,會繼續補充更新。

給大家推薦一個很讚的Linux下的命令列網址:https://www.linuxcool.com/