Shell 腳本是 Linux/Unix 系統的核心自動化工具,能夠完成以下任務:
(1)批量操作:一鍵安裝軟件、批量處理文件(重命名、壓縮、備份等)。
(2)系統管理:監控資源(CPU、內存、磁盤)、定時任務(cron)、日志分析。
(3)開發輔助:編譯自動化、環境配置、CI/CD 流水線集成。
適用讀者
(1)零基礎初學者,希望掌握 Linux 基礎操作。
(2)運維工程師,提升自動化運維能力。
(3)開發人員,優化本地開發環境和工作流程。
一、Shell基礎概念
1.1 什么是 Shell?
定義:
Shell 是用戶與操作系統內核(Kernel)之間的命令行接口,負責解析用戶輸入的命令并調用系統功能。
核心作用:
(1)執行命令和腳本
(2)管理進程和文件系統
(3)環境變量管理
(4)自動化任務處理
1.2 Shell 類型與選擇
# 查看系統支持的 Shell
cat /etc/shells
# 典型輸出:
# /bin/sh
# /bin/bash
# /usr/bin/zsh
# /bin/dash
常見 Shell 對比:
Shell 類型 | 特點 |
---|---|
Bash | Linux 默認 Shell,支持歷史命令、自動補全、數組等擴展功能 |
Zsh | 強大的交互式 Shell,支持主題和插件(如 Oh My Zsh) |
Dash | 輕量高效,適合系統啟動腳本(Ubuntu 的 /bin/sh 默認指向 Dash) |
Ksh | 兼容 POSIX 標準,適合商業環境 |
1.3 腳本執行方式
方式1:sh執行,指的是用腳本對應的sh或bash來接著腳本執行
bash script.sh
方式2: 工作目錄執行,指的是執行腳本時,先進入到腳本所在的目錄(此時,稱為工作目錄),然后使用 ./腳本方式執行
[root@localhost shell]# ./9*9.sh
-bash: ./1.sh: Permission denied
[root@localhost shell]# chmod +x 9*9.sh //賦予權限 x代表執行權限
[root@localhost shell]# ./9*9.sh //執行腳本
方式3:shell環境執行,指的是在當前的shell環境中執行,可以使用 . 接腳本 或 source 接腳本
source script.sh
方式4:絕對路徑執行,指的是直接從根目錄/到腳本目錄的絕對路徑
/root/script.sh
二、shell基礎命令
2.1 基礎命令
(1)查看腳本執行過程
[root@localhost shell]# bash -x 9*9.sh
++ seq 9
+ for i in '`seq 9`'
++ seq 1
+ for j in '`seq $i`'
+ printf '1*1=%2d ' 1
1*1= 1 + echo ''
(2)查看腳本是否有語法錯誤
[root@localhost shell]# bash -n 9*9.sh
2.2 Date命令
(1)顯示年月日
[root@localhost ~]# date +%Y-%m-%d
2024-10-08
[root@localhost ~]# date +%y-%m-%d
24-10-08
[root@localhost ~]# date +%F
2024-10-08
(2) 顯示時分秒
[root@localhost ~]# date +%T
15:04:36
[root@localhost ~]# date +%H:%M:%S
15:04:52
(3)顯示星期
[root@shell ~]# date +%w
5
[root@shell ~]# date +%W
41
(4)時間戳
使用命令date +%s 顯示從 1970 年 1 月 1 日 00:00:00 到目前為止的秒數。
[root@shell ~]# date +%s
1728608466
使用date -d@1728608466 顯示輸入描述前的時間
[root@shell ~]# date -d@1728608466
2024年 10月 11日 星期五 09:01:06 CST
(5)顯示一個小時之前、后
date -d "+1 hour" #一個小時后
date -d "-1 hour" #一個小時前
(6)顯示一天之前、之后
date -d "+1day" #一天后
date -d "-1 day" #一天前
2.3 重定向及管道
前言:標準流
文件描述符 | 名稱 | 默認目標 |
---|---|---|
0 | 標準輸入 | 鍵盤 |
1 | 標準輸出 | 屏幕 |
2 | 標準錯誤 | 屏幕 |
1.輸出重定向(>和>>)
1.1標準輸出重定向(>)
作用:將命令的標準輸出重定向到文件。如果文件不存在,則創建文件;如果文件存在,則覆蓋原有內容。
示例1:ls -l > file_list.txt,此命令會將ls -l(列出文件詳細信息)的輸出結果保存到file_list.txt文件中。如果file_list.txt之前有內容,會被覆蓋。
實例2:在script.sh腳本中,將echo命令的輸出重定向到一個文件。
1.2標準輸出追加重定向(>>)
作用:將命令的標準輸出追加到文件末尾。如果文件不存在,則創建文件。
示例:echo “New line” >> log.txt,會將字符串 “New line” 添加到log.txt文件的末尾。
1.3標準錯誤重定向(2>)
作用:將命令執行過程中產生的標準錯誤輸出重定向到文件。
示例:grep non_existent_pattern file.txt 2> error.log,當grep命令在file.txt中找不到指定模式時,產生的錯誤信息會被保存到error.log文件中,而不是顯示在終端。
1.4標準錯誤追加重定向(2>>)
作用:將標準錯誤輸出追加到文件末尾。
示例:在一個循環執行可能出錯的命令腳本中,for i in $(seq 1 5); do command_that_might_fail 2>> error_accumulate.log; done,每次循環中命令產生的錯誤信息都會被追加到error_accumulate.log文件中。
1.5合并標準輸出和標準錯誤重定向(&> 或 >&)
作用:將命令的標準輸出和標準錯誤輸出同時重定向到一個文件。
示例:find. -name “*.bak” &> output_and_error.log,find命令的所有輸出(包括正常輸出和可能出現的錯誤輸出)都會被保存到output_and_error.log文件中。
2.輸入重定向(<)
用法:將文件的內容作為命令的輸入。
示例 1:
假設有一個input.txt文件,內容為一些數字 “1 2 3 4 5”,使用sort命令對這些數字進行排序,通過輸入重定向來讀取文件內容作為輸入。
運行腳本后,會輸出排序后的數字 “1 2 3 4 5”。如果沒有輸入重定向,sort命令會等待用戶從鍵盤輸入內容進行排序。
示例 2:使用wc -l命令(用于統計行數)來統計text_file.txt文件的行數,通過輸入重定向。
3. 管道和重定向結合(|和>或>>)
用法:管道(|)用于將一個命令的輸出作為另一個命令的輸入,再結合重定向可以進一步處理輸出結果。
1.保存管道中間結果并繼續處理:
示例:cat data.txt | grep “important” > intermediate_result.txt,這個命令先通過cat和grep組合從data.txt中篩選出包含 “important” 的行,然后將這些篩選后的結果保存到intermediate_result.txt文件中。之后可以對這個文件進行其他操作,如sort intermediate_result.txt。
2.實時處理并記錄結果:
示例:tail -f access.log | grep “404” > error_404.log,tail -f access.log會實時跟蹤access.log文件的新增內容,通過管道將這些內容傳遞給grep命令來查找包含 “404” 的行,然后將這些行保存到error_404.log文件中,用于實時監控網站的 404 錯誤。
3.復雜數據處理流程:
示例:find. -type f -name “*.txt” -exec cat {} ; | awk ‘{print $1}’ | sort | uniq -c > result.txt,這個命令首先使用find命令查找當前目錄下所有的.txt文件,并將這些文件的內容通過cat命令輸出,然后通過管道將這些內容傳遞給awk命令提取每行的第一個字段,接著進行排序和去重統計,最后將結果重定向到result.txt文件中,用于分析.txt文件內容的第一個字段的統計信息。
示例 1:
先使用cat命令讀取file.txt文件內容,然后通過管道將輸出傳遞給grep命令來查找包含特定單詞(如 “error”)的行,最后將結果覆蓋寫入到error_lines.txt文件。
示例 2:
先使用ps -ef命令獲取所有進程信息,然后通過管道將輸出傳遞給grep命令查找包含特定進程名(如 “httpd”)的行,最后將結果追加到process_log.txt文件。
三、變量與數據類型
3.1 變量操作
(1) 定義與賦值
name="John" # 字符串(等號兩側不可有空格)
count=42 # 整型
files=$(ls) # 命令替換賦值
readonly PI=3.14 # 只讀變量
(2) 變量引用與刪除
echo $name # 輸出變量值
echo ${name} # 推薦寫法(明確變量邊界)
unset name # 刪除變量
(3)與用戶交互
這里相當于把用戶交互時寫的值賦予給變量n
[root@shell ~]# read -p "請輸入一個數字:" n
請輸入一個數字:20
[root@shell ~]# echo $n
20
由于沒有創造一個變量接收值,系統默認變量REPLY
[root@shell ~]# read -p "請輸入一個數字:"
請輸入一個數字:30
[root@shell ~]# echo $REPLY
30
3.2 變量作用域
類型 | 定義方式 | 作用范圍 | 生命周期 |
---|---|---|---|
局部變量 | var=value | 當前 Shell 進程 | 進程結束銷毀 |
環境變量 | export var=value | 當前進程及其子進程 | 進程結束銷毀 |
永久變量 | 寫入配置文件 | 所有新啟動的 Shell | 系統重啟有效 |
環境變量配置文件:
(1)用戶級:~/.bashrc, ~/.bash_profile
(2)系統級:/etc/profile, /etc/environment
(3)以上配置文件定義變量不是實時生效,需要resource刷新一下
3.3 特殊變量
變量 | 描述 |
---|---|
$0 | 當前腳本名稱 |
$1-$9 | 腳本參數(第1到第9個) |
${10} | 位置參數10 |
$# | 參數個數 |
$* | 所有參數(合并為一個字符串),整體作為單個字符串 |
$@ | 所有參數(保留原始分隔符),每個作為單獨字符串 |
${#*} | 傳遞到腳本中的命令行參數的個數 |
$? | 返回值,用于判斷前一命令是否執行成功,0為成功,1則失敗 |
$$ | 當前進程 PID |
$- | 傳遞到腳本中的標識 |
$_ | 之前命令的最后一個參數 |
$! | 運行在后臺的最后一個作業的進程ID(PID) |
示例:
[root@localhost ~]# vi cbd.sh
#!/bin/bash
echo "$1"
echo "第二個參數是$2"
echo "第三個參數是$3"
echo "本腳本一共有$#個參數"
echo "所有位置參數(作為單個字符串)為:"$*""
echo "所有位置參數每個作為單獨字符串)為:"$@""
echo "$0"
echo "腳本中的命令行參數的個數為:${#*}"
echo "腳本中的標識為:$-"
echo "命令的最后一個參數為:$_"
判斷上一命令是否執行成功
[root@shell ~]# echo $?
0
3.4 系統變量
變量 | 描述 | 示例值 | 使用場景 |
---|---|---|---|
$PATH | 可執行文件搜索路徑。命令所示路徑,以冒號為分割; | /usr/bin:/usr/local/bin | 添加自定義命令路徑:export PATH=$PATH:/myapp |
$HOME | 當前用戶主目錄 | /home/user | 快速訪問家目錄:cd $HOME |
$USER | 當前用戶名 | john | 日志記錄:echo “User: $USER” |
$PWD | 當前工作目錄 | /var/www | 獲取腳本所在目錄:cd $(dirname $0) |
$SHELL | 當前使用的 Shell 路徑 | /bin/bash | 檢查 Shell 類型:echo $SHELL |
$LANG | 系統語言設置 | en_US.UTF-8 | 設置腳本輸出語言:export LANG=C |
$RANDOM | 生成隨機數(0-32767) | 15872 | 創建臨時密碼:passwd=$(echo $RANDOM |
$SECONDS | 腳本已運行時間(秒) | 30 | 計算執行耗時:echo “耗時: $SECONDS 秒” |
$BASH_VERSION | Bash 版本信息 | 5.1.16(1)-release | 檢查 Bash 特性兼容性 |
[root@shell ~]# echo $SHELL #顯示當前Shell類型;
/bin/bash
[root@shell ~]# echo $PATH #命令所示路徑,以冒號為分割;
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@shell ~]# echo $PWD #顯示當前所在路徑;
/root
[root@shell ~]# echo $RANDOM #隨機生成一個 0 至 32767 的整數;
7532
四、運算符與運算
4.1 算術運算
a=10 b=3
# 方式1:$(( ))
echo $((a + b)) # 13
# 方式2:expr(需空格)+ , - , \*, /, % 加,減,乘,除,取余
expr $a + $b # 13
# 方式3:let
let "c = a * b"
echo $c # 30
# 方式4:bc(浮點運算)
echo "scale=2; 10/3" | bc # 3.33
# 方式5:[]
echo $[a+b]
4.2 比較運算符
(1) 數值比較
運算符 | 描述 | 示例 |
---|---|---|
-eq | 等于 | [ $a -eq 10 ] |
-ne | 不等于 | [[ $a -ne 5 ]] |
-gt | 大于 | [ $b -gt 2 ] |
-lt | 小于 | [ $b -lt 2 ] |
-ge | 大于等于 | [ $b -ge 2 ] |
-le | 小于等于 | [ $b -le2 ] |
(2) 字符串比較
str1="hello"
str2="world"
[ "$str1" = "hello" ] # 等于
[[ "$str1" != "$str2" ]] # 不等于
[ -z "$str" ] # 判斷空字符串
(3) 文件測試
[ -f "file.txt" ] # 是普通文件
[ -d "/tmp" ] # 是目錄
[ -x "/usr/bin/git" ] # 可執行
4.3 邏輯運算符
# 邏輯與(&&)
[ $a -gt 5 ] && [ $b -lt 10 ]
# 邏輯或(||)
[[ -f "file" ]] || echo "文件不存在"
# 邏輯非(!)
if ! [ -d "/tmp" ]; thenmkdir /tmp
fi
五、字符串和數組
5.1 Shell字符串
定義:
字符串可以由單引號’ '包圍,也可以由雙引號" "包圍,也可以不用引號。
三種形式的區別:
-
不被引號包圍的字符串:不被引號包圍的字符串中出現變量時也會被解析;字符串中不能出現空格,否則空格后邊的字符串會作為其他變量或者命令解析。
-
由單引號包圍的字符串:任何字符都會原樣輸出,在其中使用變量是無效的;字符串中不能出現單引號,對單引號進行轉義也不行。
-
由雙引號" "包圍的字符串: 如果其中包含了某個變量,那么該變量會被解析,得到該變量的值,而不是原樣輸出;字符串中可以出現雙引號。
a. 獲取字符串長度:
${#字符串名}
[root@shell ~]# rzc1="123456"
[root@shell ~]# echo ${#rzc1}
6
b.字符串拼接
將兩個字符串并排放在一起就能實現拼接。$str1 $str2這種寫法不允許變量之間有空格,但是只要加上””就可以
[root@localhost ~]# rzc2="8910jqk"
[root@localhost ~]# rzc3=$rzc1$rzc2
[root@localhost ~]# echo $rzc3
12345678910jqk
c. 字符串截取
shell截取字符串通常有兩種方式:從指定位置開始截取和從指定字符(子字符串)開始截取。
(1)從左邊開始計數時,起始數字是0(這符合程序員思維);從右邊開始計數時,起始數字是1(這符合常人思維)。計數方向不同,起始數字也不同。
(2)不管從哪邊開始計數,截取方向都是從左到右。且截取的原則為[ ),即左閉右開
使用指定位置從左邊開始截取:
[root@shell ~]# echo ${rzc1:2:2} #2代表從左往右數第二個數 2代表輸出兩個數
34
[root@shell ~]# echo ${rzc1:1:2} #1 從左往右數第一個數 2 輸出兩個數字
23
[root@shell ~]# echo ${rzc1:1:4}
2345
使用指定位置從右邊開始截取:
[root@shell ~]# echo ${rzc1:0-3:2}
45
[root@shell ~]# echo ${rzc1:0-4:2}
34
使用字符指定從左邊開始截取:
使用#號可以截取指定字符(或者子字符串)右邊的所有字符。
*是通配符的一種,表示任意長度的字符串。*字符3連起來使用的意思是:忽略左邊的所有字符,直到遇見字符3(3字符不會被截取)。
[root@shell ~]# echo ${rzc1#*3}
456
使用字符指定從右邊開始截取:
使用%號可以截取指定字符(或者子字符串)左邊的所有字符。
注意的位置,因為要截取字符3左邊的字符,而忽略3右邊的字符,所以應該位于3的右側。
[root@shell ~]# echo ${rzc1%3*}
12
5.2 字符串替換
格式:${字符串/要替換的字符/替換的字符}
[root@shell ~]# rzc4="welcome my word"
[root@shell ~]# echo $rzc4
welcome my word
[root@shell ~]# echo ${rzc4/my/you}
welcome you word
str="Hello World"
# 截取子串
echo ${str:0:5} # Hello
# 替換操作
echo ${str/World/Shell} # Hello Shell
# 分割字符串
IFS=' ' read -ra parts <<< "$str"
echo ${parts[1]} # World
5.3 shell數組
概述:
bash支持一維數組(不支持多維數組),并且沒有限定數組的大小。數組是相同類型的元素按一定順序排列的集合。 類似與 C 語言,數組元素的索引由 0 開始編號。獲取數組中的元素要利用索引,索引可以是整數或算術表達式,其值應大于或等于 0。
定義:
在 Shell 中,用括號來表示數組,數組元素用“空格”符號分割開
格式:數組名=(元素1 元素2 元素3 元素n)
1. 獲取數組元素的值:
${數組名[索引]}
[root@localhost ~]# array1=(aa bb cc dd)
[root@localhost ~]# echo ${array1[0]}
aa
[root@localhost ~]# echo ${array1[1]}
bb
[root@localhost ~]#
# 定義數組
colors=("red" "green" "blue")
# 訪問元素
echo ${colors[1]} # green
# 遍歷數組
for color in "${colors[@]}"; doecho $color
done
# 關聯數組(Bash 4+)
declare -A user=(["name"]="Alice" ["age"]=30)
echo ${user["name"]} # Alice
2. 獲取數組長度
利用@或*,可以將數組擴展成列表,然后使用#來獲取數組元素的個數。
[root@localhost ~]# echo ${#array1[@]}
4
[root@localhost ~]# echo ${#array1[*]}
4
3. 數據拼接、合并
Shell數組拼接(數組合并),將兩個數組連接成一個數組。拼接數組的思路是:先利用@或*,將數組擴展成列表,然后再合并到一起。
[root@shell ~]# array1=(aa bb cc dd)
[root@shell ~]# echo ${array1[@]}
aa bb cc dd
[root@shell ~]# array2=(ee ff gg hh)
[root@shell ~]# echo ${array2[@]}
ee ff gg hh
[root@shell ~]# array3=(${array2[@]} ${array1[@]})
[root@shell ~]# echo ${array3[@]}
ee ff gg hh aa bb cc dd
4.數組的賦值
利用索引對指定元素進行賦值
[root@localhost ~]# echo ${array1[@]}aa bb cc dd
[root@localhost ~]# array1[1]=20
[root@localhost ~]# echo ${array1[@]}aa 20 cc dd
5.數組的刪除
利用命令unset 對數組索引指定的元素進行刪除
[root@localhost ~]# echo ${array1[@]}aa 20 cc dd
[root@localhost ~]# unset array1[1]
[root@localhost ~]# echo ${array1[@]}
aa cc dd
6.數組切片
格式:${數組名[@]:索引開始:索引結束}
[root@shell ~]# echo ${array3[@]}
ee ff gg hh aa bb cc dd
[root@shell ~]# echo ${array3[@]:1:4}
ff gg hh aa
六、流程控制
6.1 If語句
腳本中常見的邏輯判斷運算符:
-f 判斷文件是否存在 eg: if [ -f filename ];
-d 判斷目錄是否存在 eg: if [ -d dir ];
-eq 等于,應用于整型比較 equal;
-ne 不等于,應用于整型比較 not equal;
-lt 小于,應用于整型比較 letter;
-gt 大于,應用于整型比較 greater;
-le 小于或等于,應用于整型比較;
-ge 大于或等于,應用于整型比較;
-a 雙方都成立(and) 邏輯表達式 –a 邏輯表達式;
-o 單方成立(or) 邏輯表達式 –o 邏輯表達式;
-z 空字符串;
-x 是否具有可執行權限
|| 單方成立;
&& 雙方都成立表達式。
1. 不帶有else
基礎結構:
if 判斷語句; thencommand
fi
[root@shell ~]# vi if1.sh
[root@shell ~]# cat if1.sh
#!/bin/bash
a=10
if [ $a -gt 4 ] #這里的gt為英文單詞greater than(大于)
then
echo ok
fi
[root@shell ~]# sh if1.sh
ok
2. 帶有else(否則的意思)
基礎結構:
if 判斷語句 ; thencommand
elsecommand
fi
[root@shell ~]# vi if2.sh
[root@shell ~]# cat if2.sh
#!/bin/bash
a=10
if [ $a -gt 4 ]
thenecho ok
else echo "not ok"
fi
[root@shell ~]# sh if2.sh
ok
3. 帶有elif(相當于再則的意思)
[root@shell ~]# vi if3.sh
[root@shell ~]# cat if3.sh
#!/bin/bash
a=3
if [ $a -gt 4 ]
thenecho ok
elif [ $a -gt 8 ]
thenecho "very ok"
elseecho "not ok"
fi
[root@shell ~]# sh if3.sh
not ok
4. 嵌套(if句里在添加if語句)
[root@localhost ~]# vi if4.sh
#!/bin/bash
a=15if [ $a -gt 4 ]
thenif [ $a -lt 20 ]
thenecho "ok"
elseecho "very ok"
fielseecho "not ok"
fi
5. 多個條件
[root@shell ~]# cat if5.sh
#!/bin/bashread -p "請輸入任志財的一百米跑步成績:" a
read -p "請輸入王帥的一百米跑步成績:" bif [ $a -gt 9 ] && [ $a -lt 11 ]
then
echo "任志財對王帥說老弟你還得練”
if [ $b -gt 12 ]
then
echo "王帥對任志財說財哥我錯了我再也不敢和你比賽了”
fi[root@shell ~]# sh if5.sh
請輸入任志財的一百米跑步成績:10
請輸入王帥的一百米跑步成績:15
任志財對王帥說老弟你還得練”if [ 15 -gt 12 ]
then
echo 王帥對任志財說財哥我錯了我再也不敢和你比賽了”
6. if邏輯判斷
shell腳本中if經常用于判斷文檔的屬性,比如判斷是普通文件還是目錄,判斷文件是否有讀、寫、執行權限等。If常用選項如下:
-e:判斷文件或目錄是否存在。
-d:判斷是不是目錄以及是否存在。
-f:判斷是不是普通文件以及是否存在。
-r:判斷是否有讀權限。
-w:判斷是否有寫權限。
-x:判斷是否可執行。
-h: file hard link(鏈接文件)
-L :file link(鏈接文件)
-b :file 塊設備文件
-c :file 字符設備文件
-p :file 管道文件
-S :file socket套接字文件
-t :file 文件與終端相關聯
-N :file文件最后一次讀取后被修改過
-s :file 文件大小不為0,文件存在且非空
-z:判斷是否為空字符串
-n:判斷是否為非空字符串
注意:root用戶對文件的讀寫比較特殊,即使一個文件沒有給root用戶讀或者寫的權限,root也可以讀或者寫。
[root@localhost ~]# vi test.sh
#!/bin/bash
if [ -d /etc ]; thenif [ -f if1.sh ]; thenif [ -r if1.sh ]; thenif [ ! -w if1.sh ]; then # !-W 表示數學中的非,即本為ture,非本則為falseecho "文件不具有寫權限"elseecho "文件具有寫權限"fiecho "文件具有讀權限"elseecho "文件不具有讀權限"fiecho "文件存在"elseecho "文件不存在"fi
echo "目錄存在"elseecho "目錄不存在"fi
6.2 case判斷
Case判斷基礎格式如下
case 變量 in
value1) #不限制value的個數
command
;; #一個分支的結束
value2)
command
;;
*) #此處*代表其他值
command
;;
esac
在Shell腳本中,case語句是一種多路選擇結構,它允許一個變量等于多個值時分別執行不同的操作。case語句以case關鍵字開始,以esac(case的反寫)關鍵字結束。
為了讓我們能夠更加清晰的理解case邏輯判斷,接下來我們編寫一個腳本來進行實驗:
[root@localhost ~]# vi case.sh
#!/bin/bash
read -p "Please input a number:" nif [ -z "$n" ]thenecho "Please input a number."exit 1fiif [ $n -lt 60 ] && [ $n -ge 1 ]thentag=1
elif [ $n -ge 60 ] && [ $n -lt 80 ]
thentag=2
elif [ $n -ge 80 ] && [ $n -lt 90 ]
thentag=3
thentag=4elsetag=0fi
case $tag in #$tag匹配為1,輸出not ok1)echo "not ok";;2)echo "ok";;3)echo "very ok";;4)echo "very good";;*) #$tag匹配除前面1,2,3,4以外的數。elif [ $n -ge 90 ] && [ $n -le 100 ]echo "The number range is 0-100.";;
esac
七、循環控制
7.1. for循環
基礎結構如下
for 變量名 in 循環條件;
do
command
done
案例:
基礎案例
[root@localhost shell]# vi for1.sh
#!/bin/bash
sum=0
for i in `seq 1 10`
dosum=$[$sum+$i]echo $i
done
echo $sum
執行結果:
進階案例(9*9乘法表)
[root@shell ~]# vi for2.sh
[root@shell ~]# cat for2.sh
#!/bin/bash
for((i=1;i<10;i++)) #定義i的初始值為1,且這個值不大于9,每次循環加1
dofor((j=1;j<=i;j++))doecho -en "$i*$j=$[i*j]\t" #-n 表示不換行打印,\t表示制表符,屬于轉義字符,-e的作用就是使轉義字符能夠被解釋 doneechodone
7.2 while循環
基礎結構
while 條件; do
command
done
用while循環制作99乘法表
[root@shell ~]# vi while.sh
[root@shell ~]# cat while.sh
#!/bin/bashi=1while (( $i<=9 ))doj=1while (( $j<=$i ))doecho -ne "$i*$j=$((i * j))\t"let j++ # let為賦值表達式,即j++ 《=》j=j+1donelet i++echo "" # 這步操作在于換行done
7.3 跳出循環
break在腳本中表示跳出該層循環,示例如下:
[root@shell ~]# vi break1.sh
[root@shell ~]# cat break1.sh
#!/bin/bash
for i in `seq 1 5`
doecho $iif [ $i -eq 3 ] thenbreak fiecho $i
done
echo aaaaa
7.4 結束本次循環
當在shell腳本中使用continue時,結束的不是整個循環,而是本次循環。忽略continue之下的代碼,直接進行下一次循環。示例如下:
[root@shell ~]# vi continue1.sh
[root@shell ~]# cat continue1.sh
#!/bin/bash
for i in `seq 1 5 `
doecho $iif [ $i == 3 ] thencontinue #此處continue表示若 $i == 3 則結束本次循環fiecho $i
done
echo $i
7.5 退出整個腳本
當我們在shell腳本中遇到exit時,其表示直接退出整個shell腳本。示例如下:
[root@shell ~]# vi exit1.sh
[root@shell ~]# cat exit1.sh
#!/bin/bash
for i in `seq 1 5`
doecho $iif [ $i == 3 ] thenexit fiecho $i
done
echo aaaa
八、函數
shell腳本中的函數就是先把一段代碼整理到了一個小單元中,并給這個小單元命名,當我們用到這段代碼時直接調用這個小單元的名字就可以了,這樣很方便,省時省力。但我們需要注意,在shell腳本中,函數一定要寫在前面,因為函數要被調用的,如果還未出現就被調用就會出錯。
基礎格式:
function f_name() # function 函數名()
{ #{
Command # 函數體(即命令序列)
} #}
8.1 打印出第一個、第二個參數、參數的個數及腳本名
示例如下:
[root@shell ~]# vi fun1.sh
[root@shell ~]# cat fun1.sh
#/bin/bash
input()
{
echo $1 $2 $# $0 # 函數的參數:$1 $2 $# ;$0則是腳本的名字
}
input 1 a b[root@shell ~]# sh fun1.sh
1 a 3 fun1.sh
8.2 加法的函數
[root@shell ~]# vi fun2.sh
[root@shell ~]# cat fun2.sh
#!/bin/bash
sum()
{
s=$[$1+$2]
echo $s
}
sum 1 2
8.3 獲得一個網卡的ip地址
[root@shell ~]# vi fun3.sh
[root@shell ~]# cat fun3.sh
#!/bin/bash
ip()
{
ifconfig |grep -A1 "$1: " |tail -1 |awk '{print $2}'
}
read -p "Please input the eth name: " e
myip=`ip $e`
echo "$e address is $myip"
8.4 返回值與傳參
add() {echo $(($1 + $2))
}
result=$(add 3 5) # 通過 $? 或命令替換獲取結果
echo "3+5=$result"
8.5 函數庫封裝
math_lib.sh:
#!/bin/bash
square() {echo $(($1 * $1))
}
1
2
3
4
主腳本:
source math_lib.sh
echo "5的平方: $(square 5)"
九、正則表達式
概述
正則表達式是你所定義的模式模板, Linux工具可以用它來過濾文本。 Linux 工具(比如sed編輯器或gawk程序)能夠在處理數據時使用正則表達式對數據進行模式匹配。如果數據匹配模式,它就會被接受并進一步處理;
如果數據不匹配模式,它就會被濾掉。
它主要用于字符串的分割,匹配、査找及替換操作。即正則表達式是一種文本模式,該模式描述在搜索文本時要匹配的一個或多個字符串
簡單來說就是通過一些特殊字符的排序,用以刪除、查找、替換一行或者多行文字字符串的程序。
1.基礎正則
基礎正則常見元字符(支持的工具:grep、egrep、sed、awk)
\ :轉義字符,用于取消特殊符號的含義,例:\! 、\n、\$等
^ :匹配字符串開始的位置,例:^a、^the、^#、^[a-z]
$ :匹配字符串結束的位置,例: word$、 ^$匹配空行
*:匹配除\n之外的任意的一個字符,例: lo.*k、lo.k、l..k
.*:匹配任意長度的字符
+:匹配前面的字符出現過最少一次
\{n\} : 匹配前面一個字符n次,例: lo\{2\}k、 '[0-9]\{2\}'匹配兩位數字
\{n,\} : 匹配前面一個字符不少于n次,例: lo\{2,\}k、 '[0-9]\{2,\}'匹配兩位及兩位以上數字
\{n,m\} : 匹配前面一個字符n到m次,例: lo\{2,3\}k、 '[0-9]\{2,3\}'匹配兩位到三位數字
注: egrep、 awk使用{n}、{n,}、 {n, m}匹配時“{}"前不用加“\”
2. 常用命令
(1)Sort排序:
以行對文件進行排序
可以通過linux自帶的man手冊去查看更多的命令
(2)查看一些常用命令可用 命令 –help
tac:倒序打印文件
rev:反向打印每一行
cut:字符切割,常用選項-d 分割,-f輸出第幾段
tr:替換或刪除字符
seq:打印序列化數字
uniq:去重 -c 打印出現次數、-u :打印不重復的行
3. 三劍客(核心)
3.1 grep
1. 功能
篩選過濾出包含<匹配字符串>的<整行>
例如:篩選出以#開頭的打印出來
2.語法
grep [選項] PATTERN [FILE....]
PATTERN:就是正則表達式,默認情況下,grep命令使用基本正則
3.選項
-i 表示忽略大小寫
[root@shell ~]# grep -i "rzc" grep.sh # “”里面表示要找的內容 后面加上文件的路徑
-E 表示啟用<擴展正則>
,將模式 PATTERN 作為一個擴展的正則表達式來解釋
[root@shell ~]# grep -E "^#|^$" grep.sh
-o只顯示匹配的行中與 PATTERN 相匹配的部分。
默認情況下,是輸出包含<匹配內容>的<一行數據>。
-v表示僅輸出<不匹配的數據行> 注意是<不匹配的行>,也就是取反
^和$代表空行
-B 打印出匹配的行之前的上文 NUM 行。
在相鄰的匹配組之間將會打印內容是 -- 的一行。
-A打印出緊隨匹配的行之后的下文NUM行。
在相鄰的匹配組之間將會打印內容是 -- 的一行。
-C顯示匹配的<數據行>的<行總數>
3.2 sed
1. 基本語法和功能
語法格式:sed [選項] ‘腳本命令’ 輸入文件。例如,sed’s/old/new/g’ file.txt,其中s表示替換操作,old是要被替換的字符串,new是替換后的字符串,g表示全局替換(即文件中所有匹配的字符串都被替換),file.txt是操作的目標文件。
功能概述:sed是一個流編輯器,主要用于對文本文件進行編輯操作。它可以在不打開文件的情況下,對文件內容進行查找、替換、刪除、插入等操作。sed的操作是基于行的,它逐行讀取文件內容,按照指定的腳本命令進行處理,然后輸出結果。
2. 替換操作 (s命令)
簡單替換:
示例:sed’s/hello/hi/’ file.txt,此命令會將file.txt文件中每行第一次出現的 “hello” 替換為 “hi”。如果要進行全局替換,需要在替換命令最后添加g選項,如sed’s/hello/hi/g’ file.txt。
使用正則表達式進行替換:
sed支持使用正則表達式來匹配要替換的內容。例如,sed’s/[0 - 9]+/number/’ file.txt,會將file.txt文件中每行出現的數字(一個或多個數字)替換為 “number”。
替換部分匹配的字符串:
可以通過在替換命令中使用&符號來引用匹配的字符串部分。例如,sed’s/(\w+), (\w+)/\2, \1/’ file.txt(在支持擴展正則表達式的情況下),假設文件中有 “John, Doe” 這樣的內容,此命令會將其轉換為 “Doe, John”,其中\1和\2分別代表第一個和第二個括號內匹配的內容。
3. 刪除操作 (d命令)
刪除匹配行:
示例:sed ‘/^#/d’ file.txt,此命令會刪除file.txt文件中所有以 “#” 開頭的行。符號表示行的開頭,所以#匹配以 “#” 開頭的行。
根據條件刪除行:
可以結合多個條件來刪除行。例如,sed -n ‘/pattern1/ { /pattern2/!d }’ file.txt(-n選項用于抑制默認輸出,只輸出經過腳本命令處理后的行),這個命令會刪除file.txt中包含 “pattern1” 但不包含 “pattern2” 的行。
4.插入和追加操作(i和a命令)
插入操作(i):
示例:sed ‘1i This is a new line’ file.txt,會在file.txt文件的第一行之前插入 “This is a new line”。
追加操作(a):
示例:sed ‘$a This is an appended line’ file.txt,會在file.txt文件的最后一行之后追加 “This is an appended line”。
5.讀取和寫入文件(r和w命令)
讀取文件(r):
示例:sed ‘3r other_file.txt’ file.txt,會在file.txt文件的第三行之后讀取并插入other_file.txt的內容。
寫入文件(w):
示例:sed -n ‘/pattern/w output_file.txt’ file.txt,會將file.txt中包含 “pattern” 的行寫入到output_file.txt文件中。
6.在shell腳本中的應用場景
配置文件修改:
在腳本中,可以使用sed修改配置文件。例如,修改nginx.conf文件中服務器監聽端口,sed -i’s/listen 80/listen 8080/g’ nginx.conf,-i選項表示直接在原文件上進行修改。
日志文件處理:
對于日志文件,可以使用sed提取或清理特定的日志行。如sed -n ‘/ERROR/p’ log_file.txt會只打印log_file.txt中包含 “ERROR” 的行,用于快速定位錯誤日志。
3.3 awk
1.打印操作
打印整行:
命令格式:awk ‘{print $0}’ 文件名。例如:awk ‘{print $0}’ log.txt會將log.txt文件中的每一行內容都打印出來。這在查看文件的原始內容時非常有用,比如查看配置文件或者簡單的文本記錄文件。
打印指定列:
命令格式:awk ‘{print $列數}’ 文件名。例如:awk ‘{print $1}’ data.csv,假設data.csv是一個以逗號分隔的文件(如包含姓名、年齡、地址等信息),這個命令會打印出每一行的第一列內容,也就是姓名列。如果想打印多列,可以用逗號分隔,如awk ‘{print $1, $3}’ file.txt會打印第一列和第三列的內容。
2.條件判斷操作
基于文本內容篩選行:
命令格式:awk ‘/正則表達式/ {動作}’ 文件名。例如:awk ‘/error/ {print $0}’ error.log會將error.log文件中包含 “error” 字樣的行全部打印出來。這在日志文件分析中經常使用,用于快速定位包含特定錯誤信息的行。
基于列內容篩選行:
命令格式:awk ‘$列數 比較運算符 值 {動作}’ 文件名。例如:awk ‘$2 > 10 {print $0}’ numbers.txt,假設numbers.txt是一個包含數字的文件,這個命令會將第二列數字大于 10 的行全部打印出來。比較運算符可以是>(大于)、<(小于)、==(等于)、>=(大于等于)、<=(小于等于)和!=(不等于)。
3.數據統計操作
計算列總和:
命令格式:awk ‘{sum += $列數} END {print sum}’ 文件名。例如:awk ‘{sum += $3} END {print sum}’ sales.csv,假設sales.csv是一個銷售數據文件,第三列是銷售額,這個命令會計算出所有行銷售額的總和并打印出來。
計算列平均值:
命令格式:awk ‘{sum += $列數; count++} END {print sum/count}’ 文件名。例如:awk ‘{sum += $4; count++} END {print sum/count}’ scores.csv,假設scores.csv是一個學生成績文件,第四列是某一科目成績,這個命令會計算出該科目成績的平均值。
4.文本格式化操作
自定義輸出格式:
命令格式:awk ‘{printf “格式化字符串”, $列數}’ 文件名。例如:awk ‘{printf “%-10s %-10s\n”, $1, $2}’ contacts.txt,假設contacts.txt是一個包含聯系人姓名和電話的文件,這個命令會將姓名和電話以左對齊、寬度為 10 個字符的格式輸出,并且每行末尾添加換行符。格式化字符串中常用的格式控制符有%s(字符串)、%d(整數)、%f(浮點數)等,-表示左對齊,數字表示寬度。
字符串拼接與替換:
命令格式:awk ‘{new_column = $列數1 “連接字符” $列數2; print new_column}’ 文件名。例如:awk ‘{new_column = $1 " - " $2; print new_column}’ products.txt,假設products.txt是一個產品信息文件,第一列是產品名稱,第二列是產品型號,這個命令會將名稱和型號用 “-” 連接起來并打印新的列。也可以使用sub或gsub函數進行字符串替換,如awk ‘{sub(“舊字符串”, “新字符串”, $列數); print $列數}’ 文件名。
十、錯誤處理與調試
10.1 錯誤捕獲
# 立即退出遇到錯誤
set -e
# 捕獲錯誤并處理
trap "echo '發生錯誤!退出狀態: $?'" ERR
# 忽略錯誤
command || true
10.2 調試技巧
# 啟用調試模式
set -x
echo "調試信息可見"
set +x
# 輸出行號
echo "當前行號: $LINENO"
10.3 日志記錄
exec > >(tee -a script.log) 2>&1
echo "日志將同時輸出到屏幕和文件"