史上最全shell指令碼程式設計語法上冊

Linux运维技术栈發表於2024-05-01

1. shell 指令碼語言的基本用法

1.1 shell 指令碼的用途
將簡單的命令組合完成複雜的工作,自動化執行命令,提高工作效率;
減少手工命令的輸入,一定程度上避免人為錯誤;
將軟體或應用的安裝及配置實現標準化;
用於實現日常性的,重複性的,非互動式的運維工作,如:檔案打包壓縮備份,監控系統執行狀態並實現告警等;

1.2 shell 指令碼基本結構
shell指令碼程式設計:是基於過程式、解釋執行的語言
程式語言的基本結構:
①各種系統命令的組合
②資料儲存:變數、陣列
③表示式:a + b
④控制語句:if
格式要求:首行shebang機制

1.3 shell指令碼建立過程
第一步:使用文字編輯器來建立文字檔案
第一行必須包括shell宣告序列:#!
示例:
#!/bin/bash
新增註釋,註釋以#開頭
第二步:加執行許可權
給予執行許可權,在命令列上指定指令碼的絕對或相對路徑
第三步:執行指令碼
直接執行直譯器,將指令碼作為直譯器程式的引數執行

1.4 shell 指令碼除錯
只檢測指令碼中的語法錯誤,但無法檢查出命令錯誤,但不真正執行指令碼:
bash -n /path/to/some_script
除錯並執行:
bash -x /path/to/some_script
指令碼錯誤常見的有三種
①語法錯誤,會導致後續的命令不繼續執行,可以用bash -n 檢查錯誤,提示的出錯行數不一定是準確的;
②命令錯誤,預設後續的命令還會繼續執行,用bash -n 無法檢查出來 ,可以使用 bash -x 進行觀察;
③邏輯錯誤:只能使用 bash -x 進行觀察;

2.1 變數
變數表示命名的記憶體空間,將資料放在記憶體空間中,透過變數名引用,獲取資料

2.1.1 變數型別
①內建變數,如:PS1,PATH,UID,HOSTNAME,$$,BASHPID,PPID,$?,HISTSIZE
②使用者自定義變數
不同的變數存放的資料不同,決定了以下
①資料儲存方式
②參與的運算
③表示的資料範圍
變數資料型別:
①字元
②數值:整型、浮點型,bash 不支援浮點數

2.1.2 Shell中變數命名法則
2.1.2.1 命名要求
區分大小寫
不能使程式中的保留字和內建變數:如:if, for
只能使用數字、字母及下劃線,且不能以數字開頭,注意:不支援短橫線 “ - ”,和主機名相反

2.1.2.2 命名習慣
見名知義,用英文單詞命名,並體現出實際作用,不要用簡寫,如:ATM
變數名大寫
區域性變數小寫
函式名小寫
大駝峰StudentFirstName
小駝峰studentFirstName
下劃線: student_name

2.1.3 變數定義和引用
變數的生效範圍等標準劃分變數型別:
普通變數:生效範圍為當前shell程序;對當前shell之外的其它shell程序,包括當前shell的子shell程序均無效;
環境變數:生效範圍為當前shell程序及其子程序;
本地變數:生效範圍為當前shell程序中某程式碼片斷,通常指函式;

變數賦值:name='value'
value 可以是以下多種形式:
直接字串:name='root'
變數引用:name="$USER"
命令引用:name=COMMAND 或者 name=$(COMMAND)
注意:變數賦值是臨時生效,當退出終端後,變數會自動刪除,無法持久儲存,指令碼中的變數會隨著腳
本結束,也會自動刪除
變數引用:
"$name" 弱引用,其中的變數引用會被替換為變數值
'$name' 強引用,其中的變數引用不會被替換為變數值,而保持原字串
範例:變數的各種賦值方式和引用


範例:變數引用

範例:變數的間接賦值和引用

範例:變數追加值

範例:利用變數實現動態命令

顯示已定義的所有變數:set
刪除變數:unset

2.1.4 環境變數
環境變數:
①可以使子程序(包括孫子程序)繼承父程序的變數,但是無法讓父程序使用子程序的變數
②一旦子程序修改從父程序繼承的變數,將會新的值傳遞給孫子程序
③一般只在系統配置檔案中使用,在指令碼中較少使用
變數宣告和賦值:
宣告並賦值
export name=VALUE
declare -x name=VALUE

或者分兩步實現
name=VALUE
export name

變數引用:
$name
${name}

顯示所有環境變數:
env
printenv
export
declare -x

檢視指定程序的環境變數
cat /proc/$PID/environ

刪除變數:
unset name

bash內建的環境變數
PATH
SHELL
USER
UID
HOME
PWD
SHLVL #shell的巢狀層數,即深度
LANG
MAIL
HOSTNAME
HISTSIZE
_ #下劃線,表示前一命令的最後一個引數

2.1.5 只讀變數
只讀變數:只能宣告定義,但後續不能修改和刪除,即常量
宣告只讀變數:
readonly name
declare -r name
檢視只讀變數:
readonly [-p]
declare -r

2.1.6 位置變數
位置變數:在bash shell中內建的變數, 在指令碼程式碼中呼叫透過命令列傳遞給指令碼的引數。
$1, $2, ... 對應第1個、第2個等引數,shift [n]換位置
$0 命令本身,包括路徑
$* 傳遞給指令碼的所有引數,全部引數合為一個字串
$@ 傳遞給指令碼的所有引數,每個引數為獨立字串
$# 傳遞給指令碼的引數的個數
注意:$@ $* 只在被雙引號包起來的時候才會有差異

清空所有位置變數
set --

2.1.7 退出狀態碼變數
當我們瀏覽網頁時,有時會看到下圖所顯示的數字,表示網頁的錯誤資訊,我們稱為狀態碼,在shell腳
本中也有相似的技術表示程式執行的相應狀態。

程序執行後,將使用變數 $? 儲存狀態碼的相關數字,不同的值反應成功或失敗,$?取值範例 0-255
$?的值為0 #代表成功
$?的值是1到255 #代表失敗

使用者可以在指令碼中使用以下命令自定義退出狀態碼
exit [n]

注意:
①指令碼中一旦遇到exit命令,指令碼會立即終止;終止退出狀態取決於exit命令後面的數字
②如果exit後面無數字,終止退出狀態取決於exit命令前面命令執行結果
③如果沒有exit命令, 即未給指令碼指定退出狀態碼,整個指令碼的退出狀態碼取決於指令碼中執行的最後一條命令的狀態碼

2.1.8 展開命令列
展開命令執行順序
把命令列分成單個命令詞
展開別名
展開大括號的宣告{}
展開波浪符宣告 ~
命令替換$() 和 ``
再次把命令列分成命令詞
展開檔案通配*、?、[abc]等等
準備I/0重導向 <、>
執行命令

防止擴充套件
反斜線(\)會使隨後的字元按原意解釋

加引號來防止擴充套件
單引號(’’)防止所有擴充套件
雙引號(”“)也可防止擴充套件,但是以下情況例外:$(美元符號) 

變數擴充套件
`` : 反引號,命令替換
\:反斜線,禁止單個字元擴充套件
!:歎號,歷史命令替換

2.1.9 指令碼安全和 set
set 命令:可以用來定製 shell 環境
$- 變數
h:hashall,開啟選項後,Shell 會將命令所在的路徑hash下來,避免每次都要查詢。透過set +h將h選項關閉;
i:interactive-comments,包含這個選項說明當前的 shell 是一個互動式的 shell。所謂的互動式shell,在指令碼中,i選項是關閉的;
m:monitor,開啟監控模式,就可以透過Job control來控制程序的停止、繼續,後臺或者前臺執行等;
B:braceexpand,大括號擴充套件;
H:history,H選項開啟,可以展開歷史列表中的命令,可以透過!感嘆號來完成,例如“!!”返回上最近的一個歷史命令,“!n”返回第 n 個歷史命令;
範例:

set 命令實現指令碼安全
-u 在擴充套件一個沒有設定的變數時,顯示錯誤資訊, 等同set -o nounset
-e 如果一個命令返回一個非0退出狀態值(失敗)就退出, 等同set -o errexit
-o option 顯示,開啟或者關閉選項
顯示選項:set -o
開啟選項:set -o 選項
關閉選項:set +o 選項
-x 當執行命令時,列印命令及其引數,類似 bash -x
範例:

範例:
DIR=/data
cd $DIR
rm -rf *
#rm -rf $DIr/*

2.2 格式化輸出 printf
格式
printf "指定的格式" "文字1" ”文字2“……
常用格式替換符

說明:
%#s 中的數字代表此替換符中的輸出字元寬度,不足補空格,預設是右對齊,%-10s表示10個字元寬,- 表示左對齊;
%03d 表示3位寬度,不足前面用0補全,超出位數原樣輸出;
%.2f 中的2表示小數點後顯示的小數位數;
常用跳脫字元

範例:

#.2f 表示保留兩位小數

#%-10s 表示寬度10個字元,左對齊

#將十進位制的17轉換成16進位制數

#將十六進位制C轉換成十進位制

2.3 算術運算
Shell允許在某些情況下對算術表示式進行求值,比如:let和declare 內建命令,(( ))複合命令和算術擴
展。求值以固定寬度的整數進行,不檢查溢位,儘管除以0 被困並標記為錯誤。運算子及其優先順序,關
聯性和值與C語言相同。以下運算子列表分組為等優先順序運算子級別。級別按降序排列優先。
注意:bash 只支援整數,不支援小數

乘法符號有些場景中需要轉義
實現算術運算:
(1) let var=算術表示式
(2) ((var=算術表示式)) 和上面等價
(3) var=$[算術表示式]
(4) var=$((算術表示式))
(5) var=$(expr arg1 arg2 arg3 ...)
(6) declare -i var = 數值
(7) echo '算術表示式' | bc
內建的隨機數生成器變數:
$RANDOM 取值範圍:0-32767
範例:
#生成 0 - 49 之間隨機數
echo $[$RANDOM%50]
#隨機字型顏色

增強型賦值:

格式:
let varOPERvalue

範例:
自加3後自賦值

範例:
自增,自減

範例:

2.4 邏輯運算
true, false
1,真
0,假
與:& 和0相與結果為0,和1相與結果保留原值, 一假則假,全真才真
0 與 0 = 0
0 與 1 = 0
1 與 0 = 0
1 與 1 = 1
範例:

或:| 和1相或結果為1,和0相或結果保留原值,一真則真,全假才假
0 或 0 = 0
0 或 1 = 1
1 或 0 = 1
1 或 1 = 1
範例:

非:!
! 1 = 0 ! true
! 0 = 1 ! false
異或:^
異或的兩個值,相同為假,不同為真。兩個數字X,Y異或得到結果Z,Z再和任意兩者之一X異或,將得出另一個值Y
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0
範例:

範例: 變數互換

短路運算
短路與 &&
CMD1 短路與 CMD2
第一個CMD1結果為真 (1),第二個CMD2必須要參與運算,才能得到最終的結果
第一個CMD1結果為假 (0),總的結果必定為0,因此不需要執行CMD2

短路或 ||
CMD1 短路或 CMD2
第一個CMD1結果為真 (1),總的結果必定為1,因此不需要執行CMD2
第一個CMD1結果為假 (0),第二個CMD2 必須要參與運算,才能得到最終的結果

相關文章