此文章為學習了 尚硅谷B站課程?后的學習筆記
【尚硅谷】Shell腳本從入門到實戰_嗶哩嗶哩_bilibilihttps://www.bilibili.com/video/BV1hW41167NW/?spm_id_from=333.337.search-card.all.click&vd_source=68e0bbe20c8b1102b59ced40f67db628注意:需要先學Linux基礎命令后,學習shell命令
Linux常用基礎命令-CSDN博客https://blog.csdn.net/2302_78022640/article/details/149337415?spm=1011.2415.3001.5331
零基礎 Shell 教學全集:全面入門、精講實例、實戰腳本
第 1 章:什么是Shell
Shell 是一個命令行解釋器
Shell 是?用戶與 Linux 內核交互的核心接口,兼具?命令解釋器?和?腳本編程語言?的雙重身份,也可理解為包裹內核的 “保護殼”(隔離用戶直接操作內核,保障系統安全)。以下從核心邏輯、功能特性、常見版本展開解析:
核心作用:連接用戶與內核
- 命令中轉:接收用戶輸入的指令(如?
ls
?查看文件、cd
?切換目錄),將其轉換為內核能理解的格式,驅動內核執行操作,再把結果反饋給用戶。 - 安全隔離:用戶無法直接訪問內核,必須通過 Shell 交互,避免誤操作破壞系統核心,就像 “門衛” 守護內核。
1.1 常見 Shell 解析器
Linux 系統提供了多種 Shell 解析器,我們可以通過以下命令查看系統中可用的 Shell 解析器:
cat /etc/shells
輸出可能為:
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
/bin/tcsh
/bin/csh
這表示當前系統中安裝了這些 Shell 解析器,其中?bash
?是最常用的默認的一種。
1.2 bash 和 sh 的關系
在很多 Linux 系統中,sh
?實際上是?bash
?的一個鏈接(符號鏈接),我們可以通過以下命令查看它們的關系:
(本文章第九章有說明grep的使用,正則表達式)
ll | grep bash
含義是:
-
ll
:是ls -l
的別名,列出當前目錄下的文件詳細信息(包括權限、所有者、大小、修改時間等)。 -
grep bash
:從ll
的輸出中篩選出包含 “bash” 的行。
輸出:
這里的?lrwxrwxrwx
?表示?sh
?是一個符號鏈接,箭頭?->
?后面的?bash
?表示它鏈接到?bash
,這意味著執行?sh
?命令實際上等同于執行?bash
?命令。
1.3 查看當前默認 shell
要查看當前系統默認使用的 Shell 解析器,可以使用以下命令:
echo $SHELL
在 CentOS 系統中,輸出通常為:
/bin/bash
這表明 CentOS 默認的 Shell 解析器是?bash
。
第 2 章:Shell 腳本入門
2.1 腳本格式要求
編寫 Shell 腳本時,有一個固定的格式要求:腳本必須以?#!/bin/bash
?開頭,這一行的作用是指定腳本使用?bash
?解析器來執行。同時,為了便于識別,Shell 腳本通常使用?.sh
?作為后綴名。
可以先建一個文件夾:
mkdir codes
cd codes
如圖例:
2.2 Hello World 示例
下面我們來編寫第一個 Shell 腳本,實現輸出 "helloworld" 的功能。
步驟:
- 創建腳本文件:
touch helloworld.sh
- 編輯腳本內容:
(打開后按 “i” 開始編寫,寫完后按 “Esc” ,再輸入 “:wq” 出來)(下面這步不會的請先學linux基礎命令)vim helloworld.sh
- 在打開的文件中輸入以下內容:
#!/bin/bash echo "helloworld"
2.3 三種執行方式
Shell 腳本有多種執行方式,不同的方式有不同的特點和使用場景。
第一種:不需要執行權限(bash/sh + 路徑)
這種方式是通過?bash
?或?sh
?命令來執行腳本,不需要給腳本賦予可執行權限。可以使用相對路徑或絕對路徑指定腳本位置:
- 相對路徑(在腳本所在目錄執行)(cd 轉目錄):
sh ./helloworld.sh bash ./helloworld.sh
- 絕對路徑(可以在任意目錄執行):
sh /home/atguigu/shells/helloworld.sh bash /home/atguigu/shells/helloworld.sh
這種方式的本質是?bash
?或?sh
?解析器幫我們執行腳本,所以腳本本身不需要可執行權限。
第二種:需要執行權限(直接執行)
這種方式是直接執行腳本文件,需要先給腳本賦予可執行權限:
(可以看到什么圖片中helloworld.sh 文件被加粗了)
- 賦予權限(此為linux基礎命令):
chmod +x helloworld.sh
- 執行腳本:
- 相對路徑(在腳本所在目錄執行):
./helloworld.sh
- 絕對路徑(可以在任意目錄執行):
/home/atguigu/shells/helloworld.sh
- 相對路徑(在腳本所在目錄執行):
這種方式的本質是腳本自己執行,所以必須要有可執行權限。
第三種:使用?source
?或?.
(當前 shell 執行)
使用?source
?命令或?.
(點號)執行腳本,腳本會在當前 Shell 環境中執行,而不是創建一個新的子 Shell 環境。這會導致腳本中定義的變量在腳本執行后仍然有效。
例如,有一個?test.sh
?腳本:
cat test.sh
內容如下:
#!/bin/bash
A=5
echo $A
分別用不同方式執行:
bash test.sh
sh test.sh
./test.sh
. test.sh # 或 source test.sh
echo $A # 只有 source 執行才能打印 5
前三種方式(bash
、sh
、直接執行)都會創建子 Shell 來執行腳本,腳本執行結束后子 Shell 關閉,在子 Shell 中定義的變量不會影響父 Shell;而使用?source
?或?.
?執行腳本時,腳本在當前 Shell 中執行,變量會保留在當前 Shell 中。
第 3 章:變量
在 Shell 中,變量是存儲數據的容器,我們可以通過變量名來訪問存儲的數據。Shell 中的變量分為系統預定義變量、自定義變量和特殊變量。
3.1 系統變量示例
系統預定義變量是系統自帶的變量,用于存儲系統相關的信息。常見的系統變量有:
$HOME
:當前用戶的家目錄$PWD
:當前工作目錄$SHELL
:當前默認的 Shell 解析器$USER
:當前登錄的用戶
可以通過?echo
?命令查看這些變量的值:
echo $HOME
echo $PWD
echo $SHELL
echo $USER
使用?set
?命令可以顯示當前 Shell 中的所有變量:
set
這些系統變量為我們獲取系統信息提供了方便。
3.2 自定義變量
除了系統預定義變量,我們還可以自己定義變量來存儲數據。
定義和使用變量
定義變量的基本格式是:變量名=變量值
,注意等號前后不能有空格。使用變量時,在變量名前加?$
?符號。
A=5
echo $A # 輸出 5
重新賦值和撤銷變量
可以對已定義的變量重新賦值:
A=8
echo $A # 輸出 8
使用?unset
?命令可以撤銷變量:
unset A
echo $A # 變量 A 已被撤銷,無輸出
只讀變量
使用?readonly
?命令可以定義只讀變量,只讀變量不能被重新賦值,也不能被撤銷:
readonly B=2
echo $B # 輸出 2
B=9 # 報錯:readonly variable
變量的類型
在 bash 中,變量默認都是字符串類型,即使看起來是數字,也不能直接進行數值運算:
C=1+2
echo $C # 輸出:1+2,而不是 3
如果變量的值包含空格,需要使用雙引號或單引號將其括起來:
D=I love banzhang # 錯誤,會被解析為多個命令
D="I love banzhang" # 正確
echo $D # 輸出:I love banzhang
設置全局變量(供腳本調用)
默認情況下,自定義變量只在當前 Shell 中有效,在子 Shell 或腳本中無法訪問。如果要讓變量在子 Shell 或腳本中也能使用,需要使用?export
?命令將其提升為全局變量:
export B
例如,我們在?helloworld.sh
?腳本中添加輸出變量?B
?的語句:
vim helloworld.sh
內容添加:
#!/bin/bash
echo "helloworld"
echo $B
運行腳本:
./helloworld.sh
如果沒有執行?export B
?命令,腳本中不會輸出變量?B
?的值;執行?export B
?后重新執行腳本,就可以輸出?B
?的值了。
3.3 特殊變量
特殊變量是 Shell 中具有特殊含義的變量,用于處理命令行參數、命令執行狀態等。
$n
、$#
$n
:用于獲取命令行參數,$0
?代表腳本名稱,$1
?到?$9
?代表第 1 到第 9 個參數,10 及以上的參數需要用?${10}
?表示。$#
:用于獲取命令行參數的個數。
示例:
vim parameter.sh
內容:
#!/bin/bash
echo '==========$n=========='
echo $0 # 腳本名稱
echo $1 # 第一個參數
echo $2 # 第二個參數
echo '==========$#=========='
echo $# # 參數個數
執行:
chmod 777 parameter.sh
./parameter.sh cls xz
輸出:
==========$n==========
./parameter.sh
cls
xz
==========$#==========
2
可以看到,$0
?輸出了腳本名稱?./parameter.sh
,$1
?輸出了第一個參數?cls
,$2
?輸出了第二個參數?xz
,$#
?輸出了參數的個數?2
。
$*
?和?$@
$*
?和?$@
?都用于獲取所有命令行參數,但它們在處理參數的方式上有所不同:
$*
:將所有參數視為一個整體。$@
:將每個參數視為獨立的個體。
示例:
vim parameter.sh
追加內容:
echo '==========$*=========='
echo $*
echo '==========$@=========='
echo $@
執行:
./parameter.sh a b c d e f g
輸出:
==========$*==========
a b c d e f g
==========$@==========
a b c d e f g
從表面上看,$*
?和?$@
?的輸出結果相同,但在循環中它們的表現不同。我們可以通過一個循環示例來觀察:
vim for4.sh
#!/bin/bash
echo '=============$*============='
for i in "$*"
doecho "ban zhang love $i"
doneecho '=============$@============='
for j in "$@"
doecho "ban zhang love $j"
done
執行:
chmod 777 for4.sh
./for4.sh cls mly wls
輸出:
=============$*=============
ban zhang love cls mly wls
=============$@=============
ban zhang love cls
ban zhang love mly
ban zhang love wls
可以看到,"$*"
?被當作一個整體進行循環,只循環一次;而?"$@"
?被當作多個獨立的參數,循環次數與參數個數相同。
$?
$?
?用于獲取上一個命令執行的返回狀態。如果返回值為 0,表示上一個命令執行成功;如果返回值為非 0,則表示上一個命令執行失敗。
例如,判斷?helloworld.sh
?腳本是否正確執行:
./helloworld.sh
echo $? # 輸出 0,說明執行成功
這在腳本中用于判斷命令是否執行成功非常有用。
第 4 章:運算符
在 Shell 中進行數值運算時,不能直接使用?+
、-
、*
、/
?等運算符,需要使用特定的格式。
Shell 中常用的數值運算格式有兩種:
$((運算式))
$[運算式]
例如,計算?(2+3)*4
?的值:
S=$[(2+3)*4]
echo $S # 輸出 20
或者:
S=$(( (2+3)*4 ))
echo $S # 輸出 20
這兩種格式都可以實現數值運算,使用起來比較方便。
第 5 章:條件判斷
在 Shell 中,條件判斷用于根據不同的條件執行不同的操作。
5.1 條件判斷的基本語法
Shell 中條件判斷有兩種基本語法:
test condition
[ condition ]??
(condition
?前后要有空格)
如果條件成立,返回 0(表示真);條件不成立,返回非 0(表示假)。需要注意的是,條件非空即為真,[ atguigu ]
?返回真,[ ]
?返回假。
5.2 常用判斷條件及示例
數值比較
用于兩個整數之間的比較,常用的運算符有:
-eq
:等于-ne
:不等于-lt
:小于-gt
:大于-le
:小于等于-ge
:大于等于
示例:判斷 23 是否大于等于 22
[ 23 -ge 22 ]
echo $? # 輸出 0,表示條件成立
文件權限判斷
用于判斷文件是否具有某種權限,常用的運算符有:
-r
:有讀權限-w
:有寫權限-x
:有執行權限
示例:判斷?helloworld.sh
?是否具有寫權限
[ -w helloworld.sh ]
echo $? # 輸出 0,表示有寫權限
文件存在性判斷
用于判斷文件或目錄是否存在,常用的運算符有:
-e
:文件或目錄存在-f
:文件存在且是普通文件-d
:文件存在且是目錄
示例:判斷?/home/atguigu/cls.txt
?是否存在
[ -e /home/atguigu/cls.txt ]
echo $? # 如果文件不存在,輸出非 0
多條件判斷
可以使用?&&
?和?||
?進行多條件判斷:
&&
:表示前一條命令執行成功時,才執行后一條命令||
:表示前一條命令執行失敗時,才執行后一條命令
示例:
[ atguigu ] && echo OK || echo notOK # 輸出 OK,因為條件非空為真
[ ] && echo OK || echo notOK # 輸出 notOK,因為條件為空為假
這在腳本中用于復雜的條件判斷非常有用。
第 6 章:流程控制
流程控制用于根據不同的條件執行不同的代碼塊,或重復執行某個代碼塊。Shell 中的流程控制包括 if 判斷、case 語句、for 循環和 while 循環。
6.1 if 判斷
if 判斷用于根據條件執行不同的代碼塊,分為單分支和多分支兩種形式。
語法格式
-
單分支:
?if [ 條件判斷式 ]; then程序 fi
或者
if [ 條件判斷式 ] then程序 fi
-
多分支:
if [ 條件判斷式 ]; then程序 elif [ 條件判斷式 ]; then程序 else程序 fi
注意事項:
[ 條件判斷式 ]
?中,中括號和條件判斷式之間必須有空格if
?后要有空格
示例
例如,輸入數字 1 輸出特定內容,輸入數字 2 輸出另一內容:
vim if.sh
($1為第一個帶入的參數)
#!/bin/bash
if [ $1 -eq 1 ]; thenecho "banzhang zhen shuai"
elif [ $1 -eq 2 ]; thenecho "cls zhen mei"
fi
chmod 777 if.sh
./if.sh 1 # 輸出 banzhang zhen shuai
6.2 case 語句
case 語句用于多分支判斷,根據變量的不同取值執行不同的代碼塊。
語法格式
case $變量名 in
"值1")如果變量的值等于值1,則執行此程序;;
"值2")如果變量的值等于值2,則執行此程序;;
...省略其他分支...
*)如果變量的值都不是以上的值,則執行此程序;;
esac
注意事項:
- case 行尾必須為單詞 “in”
- 每一個模式匹配必須以右括號 “)” 結束
- 雙分號 “;;” 表示命令序列結束,相當于 Java 中的 break
- 最后的 “*)” 表示默認模式,相當于 Java 中的 default
示例
例如,輸入數字 1 輸出 "banzhang",輸入數字 2 輸出 "cls",輸入其他數字輸出 "renyao":
vim case.sh
#!/bin/bash
case $1 in
"1")echo "banzhang";;
"2")echo "cls";;
*)echo "renyao";;
esac
chmod 777 case.sh
./case.sh 1 # 輸出 banzhang
6.3 for 循環
for 循環用于重復執行某個代碼塊,有兩種語法格式。
C 語言風格
語法格式:
for ((初始值; 循環控制條件; 變量變化)); do程序
done
示例:計算 1 到 100 的和
vim for1.sh
#!/bin/bash
sum=0
for((i=0;i<=100;i++))
dosum=$[$sum+$i]
done
echo $sum # 輸出 5050
chmod 777 for1.sh
./for1.sh
列表模式
語法格式:
for 變量 in 值1 值2 ...; do程序
done
示例:遍歷輸出列表中的元素
vim for2.sh
#!/bin/bash
for i in cls mly wls
doecho "ban zhang love $i"
done
chmod 777 for2.sh
./for2.sh
輸出:
ban zhang love cls
ban zhang love mly
ban zhang love wls
對比 $* 和 $@
前面我們已經介紹了?$*
?和?$@
?的區別,這里通過循環進一步展示:
vim for3.sh
#!/bin/bash
echo '=============$*============='
for i in $*
doecho "ban zhang love $i"
doneecho '=============$@============='
for j in $@
doecho "ban zhang love $j"
done
執行:
chmod 777 for3.sh
./for3.sh cls mly wls
當不使用雙引號時,$*
?和?$@
?的輸出完全一致。
再測試雙引號版本:
vim for4.sh
#!/bin/bash
echo '=============$*============='
for i in "$*"
doecho "ban zhang love $i"
doneecho '=============$@============='
for j in "$@"
doecho "ban zhang love $j"
done
執行:
chmod 777 for4.sh
./for4.sh cls mly wls
輸出:
=============$*=============
ban zhang love cls mly wls
=============$@=============
ban zhang love cls
ban zhang love mly
ban zhang love wls
可以更清楚地看到?$*
?和?$@
?的區別:"$*"
?將所有參數視為一個整體,"$@"
?將每個參數視為獨立個體。
6.4 while 循環
while 循環用于在條件成立時重復執行某個代碼塊。
語法格式:
while [ 條件判斷式 ]; do程序
done
示例:計算 1 到 100 的和
vim while.sh
#!/bin/bash
sum=0
i=1
while [ $i -le 100 ]
dosum=$[$sum+$i]i=$[$i+1]
done
echo $sum # 輸出 5050
chmod 777 while.sh
./while.sh
第 7 章:輸入內容到控制臺(read 讀取)
read
?命令用于從控制臺讀取用戶輸入,并將輸入的值賦給變量。
7.1 read 命令基本語法
read -t 秒數 -p "提示信息" 變量名
選項說明:
-p
:指定讀取輸入時的提示信息-t
:指定等待輸入的時間(單位:秒),如果超過指定時間沒有輸入,read
?命令會退出- 默認情況下,
read
?命令會無限等待用戶輸入
示例:等待 7 秒輸入姓名
文件:read.sh
-
創建并編輯腳本:
touch read.sh vim read.sh
-
輸入以下內容:
#!/bin/bash read -t 7 -p "Enter your name in 7 seconds :" NN echo $NN
執行:
chmod +x read.sh
./read.sh
輸出示例:
Enter your name in 7 seconds : atguigu # 在 7 秒內輸入姓名
atguigu # 輸出輸入的姓名
(自己輸入啊,記得自己輸進去啊,別干等著)
如果在 7 秒內沒有輸入,腳本會繼續執行,echo $NN
?不會輸出任何內容。
第 8 章:函數
函數是一段可以重復調用的代碼塊,用于實現特定的功能。Shell 中的函數分為系統函數和自定義函數。
8.1 系統函數
系統函數是系統自帶的函數,可以直接使用。
8.1.1?basename
:提取文件名
basename
?命令用于從路徑中提取文件名,語法格式:
basename [string / pathname] [suffix]
suffix
?為后綴,如果指定了?suffix
,basename
?會將文件名中的?suffix
?去掉。
示例:
basename /home/atguigu/banzhang.txt # 輸出 banzhang.txt
basename /home/atguigu/banzhang.txt .txt # 輸出 banzhang
basename
?可以理解為取路徑里的文件名稱。
8.1.2?dirname
:提取目錄路徑
dirname
?命令用于從路徑中提取目錄部分,語法格式:
dirname 文件絕對路徑
示例:
dirname /home/atguigu/banzhang.txt # 輸出 /home/atguigu
dirname
?可以理解為取文件路徑的絕對路徑名稱。
8.2 自定義函數
除了系統函數,我們還可以自己定義函數來實現特定的功能。
語法格式
function 函數名() {語句[ return 整數 ]
}
注意事項:
- Shell 函數必須先定義再調用,因為 Shell 腳本是逐行執行的,不會像其他語言一樣先編譯
- 函數的返回值只能通過?
$?
?系統變量獲取,可以使用?return
?語句指定返回值,如果不使用?return
?語句,函數會以最后一條命令的執行結果作為返回值,返回值的范圍是 0 到 255
示例:兩個數求和
-
創建并編輯腳本:
touch fun.sh vim fun.sh
-
輸入以下內容:
#!/bin/bashfunction sum() {s=0s=$[$1 + $2]echo "$s" }read -p "Please input the number1: " n1 read -p "Please input the number2: " n2 sum $n1 $n2
-
執行腳本:
chmod +x fun.sh ./fun.sh
-
輸出示例:
Please input the number1: 2 Please input the number2: 5 7
第 9 章:正則表達式入門
正則表達式是一種用于描述字符串模式的工具,它可以用來匹配、檢索和替換符合特定模式的字符串。在 Linux 中,grep
、sed
、awk
?等文本處理工具都支持正則表達式。
9.1 常規匹配
一串不包含特殊字符的正則表達式會匹配它自己。例如,要在?/etc/passwd
?文件中查找包含?atguigu
?的行,可以使用:
cat /etc/passwd | grep atguigu
這個命令會匹配所有包含?atguigu
?字符串的行。
9.2 常用特殊字符
1)^:行首匹配
^
?用于匹配一行的開頭。例如,要在?/etc/passwd
?文件中查找以?a
?開頭的行:
cat /etc/passwd | grep ^a
這個命令會匹配所有以?a
?開頭的行。
2)$:行尾匹配
$
?用于匹配一行的結尾。例如,要在?/etc/passwd
?文件中查找以?t
?結尾的行
cat /etc/passwd | grep t$
這個命令會匹配所有以?t
?結尾的行🔷1-297🔷。
思考:
^$
?匹配什么?
^$
?匹配空行,因為?^
?表示行首,$
?表示行尾,行首和行尾之間沒有任何字符,就是空行。
3).:匹配任意一個字符
.
?用于匹配任意一個字符(除了換行符)。例如,要在?/etc/passwd
?文件中查找包含?r
?開頭、t
?結尾,中間有兩個任意字符的行:
cat /etc/passwd | grep r..t
這個命令會匹配包含?root
、rabt
?等形式的行。
4)*:上一個字符重復 0 次或多次
*
?不能單獨使用,它需要和上一個字符連用,表示匹配上一個字符 0 次或多次。例如,要在?/etc/passwd
?文件中查找包含?ro*t
?的行:
cat /etc/passwd | grep ro*t
這個命令會匹配包含?rt
、rot
、root
、rooooot
?等形式的行。
思考:
.*
?匹配什么?
.*
?表示匹配任意長度的任意字符(除了換行符),因為?.
?匹配任意一個字符,*
?表示上一個字符(即?.
)重復 0 次或多次。
5)[]:字符集合
[]
?表示匹配某個范圍內的一個字符。例如:
[6,8]
:匹配 6 或者 8[0-9]
:匹配一個 0 到 9 的數字[0-9]*
:匹配任意長度的數字字符串[a-z]
:匹配一個 a 到 z 之間的小寫字母[a-z]*
:匹配任意長度的小寫字母字符串[a-c, e-f]
:匹配 a 到 c 或者 e 到 f 之間的任意字符
示例:
cat /etc/passwd | grep r[a,b,c]*t
這個命令會匹配包含?rt
、rat
、rbt
、rabt
?等形式的行。
6)\:轉義字符
\
?用于轉義特殊字符,使其失去特殊含義,僅表示字符本身。由于很多特殊字符在 Shell 中有特定的含義,當我們想匹配這些特殊字符本身時,需要使用?\
?進行轉義。例如,要匹配包含?a$b
?的行:
cat /etc/passwd | grep 'a\$b'
注意需要使用單引號將正則表達式引起來,防止?$
?被 Shell 解釋為變量引用。
第 10 章:文本處理工具
文本處理工具用于對文本文件進行切割、分析、提取等操作,在 Shell 腳本中經常用到。
10.1 cut 命令
cut
?命令的作用是從文件的每一行中切割出指定的字節、字符或字段,并將其輸出。
1)基本語法:
cut [選項] 文件名
cut
?命令的默認分隔符是制表符(\t
)。
2)選項說明:
選項 | 功能 |
---|---|
-f | 用于指定要提取的列號,例如?-f 1 ?表示提取第 1 列 |
-d | 用于指定分隔符,默認是制表符,例如?-d " " ?表示使用空格作為分隔符 |
-c | 按字符進行切割,后面跟數字表示取第幾個字符,例如?-c 1 ?表示取第 1 個字符 |
示例 1:準備數據
創建?cut.txt
?文件并輸入以下內容:
touch cut.txt
vim cut.txt
內容:
dong shen
guan zhen
wo wo
lai lai
le le
示例 2:提取第一列
使用空格作為分隔符,提取第 1 列:
cut -d " " -f 1 cut.txt
輸出:
dong
guan
wo
lai
le
示例 3:提取第 2、3 列
使用空格作為分隔符,提取第 2 列和第 3 列:
cut -d " " -f 2,3 cut.txt
輸出:
shen
zhen
wo
lai
le
示例 4:查找 guan 并切出
先使用?grep
?命令查找包含?guan
?的行,再使用?cut
?命令提取第 1 列:
cat cut.txt | grep guan | cut -d " " -f 1
輸出:
guan
示例 5:PATH 從第 3 個目錄起截取
PATH
?環境變量存儲了系統的命令搜索路徑,各路徑之間用冒號(:
)分隔。要從第 3 個路徑開始截取:
echo $PATH | cut -d ":" -f 3-
-f 3-
?表示從第 3 列開始提取,直到最后一列。
示例 6:提取 IP 地址
可以結合?ifconfig
、grep
?和?cut
?命令提取 IP 地址:
ifconfig ens33 | grep netmask | cut -d " " -f 10
這個命令的作用是:先通過?ifconfig ens33
?獲取網卡?ens33
?的信息,然后使用?grep netmask
?過濾出包含?netmask
?的行(通常這一行包含 IP 地址),最后使用?cut
?命令以空格為分隔符提取第 10 列,即 IP 地址。
10.2 awk 命令
awk
?是一個強大的文本分析工具,它將文件逐行讀入,以空格為默認分隔符將每行切割成多個字段,然后對這些字段進行分析處理。
1)基本語法:
awk -F 分隔符 '/模式/{動作}' 文件
模式(pattern)
:表示?awk
?要在數據中查找的內容動作(action)
:表示當找到匹配的模式時要執行的命令
2)選項說明:
選項 | 功能 |
---|---|
-F | 指定輸入文件的分隔符 |
-v | 賦值一個用戶定義變量 |
示例 1:準備 passwd
將?/etc/passwd
?文件復制到當前目錄,用于測試:
sudo cp /etc/passwd ./
passwd
?文件中每一行的格式為:用戶名:密碼(加密): 用戶 ID: 組 ID: 注釋:用戶家目錄:Shell 解析器。
示例 2:匹配 root 并輸出第 7 列
使用?:
?作為分隔符,查找以?root
?開頭的行,并輸出第 7 列(Shell 解析器):
awk -F : '/^root/{print $7}' passwd
輸出:
/bin/bash
示例 3:輸出第 1 和第 7 列(用逗號分隔)
使用?:
?作為分隔符,查找以?root
?開頭的行,并輸出第 1 列(用戶名)和第 7 列(Shell 解析器),中間用逗號分隔:
awk -F : '/^root/{print $1","$7}' passwd
輸出:
root,/bin/bash
示例 4:添加列名與尾行
使用?BEGIN
?和?END
?關鍵字可以在處理文件內容之前和之后執行指定的動作:
BEGIN
:在讀取任何數據行之前執行END
:在處理完所有數據行之后執行
示例:給?passwd
?文件的第 1 列和第 7 列添加列名,并在最后添加一行自定義內容:
awk -F : 'BEGIN{print "user, shell"} {print $1","$7} END{print "dahaige,/bin/zuishuai"}' passwd
輸出中,BEGIN
?塊輸出的內容會在文件內容之前顯示,END
?塊輸出的內容會在文件內容之后顯示。
示例 5:將 UID +1 輸出
使用?-v
?選項定義變量?i=1
,然后將?passwd
?文件中第 3 列(用戶 ID)的值加 1 并輸出:
awk -v i=1 -F : '{print $3+i}' passwd
示例 6:使用內置變量
awk
?有一些內置變量,用于獲取文件相關的信息:
變量 | 說明 |
---|---|
FILENAME | 當前處理的文件名 |
NR | 已讀取的記錄數(行號) |
NF | 當前記錄的字段個數(切割后列的個數) |
示例:統計?passwd
?文件的文件名、每行的行號和每行的列數:
awk -F : '{print "filename:" FILENAME ",linenum:" NR ",col:" NF}' passwd
示例 7:查找空行行號
查找?ifconfig
?命令輸出結果中的空行,并打印空行的行號:
ifconfig | awk '/^$/{print NR}'
/^$/
?是一個正則表達式,表示匹配空行,NR
?變量表示當前行的行號。
示例 8:提取 IP 地址
結合?ifconfig
?命令和?awk
?提取 IP 地址:
ifconfig ens33 | awk '/netmask/ {print $2}'
這個命令的作用是:先獲取網卡?ens33
?的信息,然后過濾出包含?netmask
?的行,最后輸出該行的第 2 個字段,即 IP 地址。
第 11 章:綜合應用案例
綜合應用案例將前面所學的知識結合起來,實現實際的功能。
11.1 歸檔腳本
該腳本的功能是每天對指定的目錄進行歸檔備份,將目錄下的所有文件按天歸檔保存,并將歸檔文件放在?/root/archive
?目錄下,歸檔文件的名稱格式為?archive_目錄名_日期.tar.gz
。
使用?tar
?命令進行歸檔,-c
?選項表示歸檔,-z
?選項表示同時進行壓縮,得到的文件后綴名為?.tar.gz
。
腳本內容如下:
#!/bin/bash# 首先判斷輸入參數個數是否為 1
if [ $# -ne 1 ]
thenecho "參數個數錯誤!應該輸入一個參數,作為歸檔目錄名"exit
fi# 從參數中獲取目錄名稱
if [ -d $1 ]
thenecho
elseechoecho "目錄不存在!"echoexit
fiDIR_NAME=$(basename $1)
DIR_PATH=$(cd $(dirname $1); pwd)# 獲取當前日期
DATE=$(date +%y%m%d)# 定義生成的歸檔文件名稱
FILE=archive_${DIR_NAME}_$DATE.tar.gz# 定義歸檔文件的保存路徑
DEST=/root/archive/$FILE# 開始歸檔目錄文件
echo "開始歸檔..."
echotar -czf $DEST $DIR_PATH/$DIR_NAMEif [ $? -eq 0 ]
thenechoecho "歸檔成功!"echo "歸檔文件為:$DEST"echo
elseecho "歸檔出現問題!"echo
fiexit
腳本說明:
- 首先判斷輸入參數的個數是否為 1,如果不是,提示錯誤并退出
- 判斷輸入的參數是否是一個存在的目錄,如果不是,提示錯誤并退出
- 使用?
basename
?命令獲取目錄的名稱,使用?cd
?和?pwd
?命令獲取目錄的絕對路徑 - 使用?
date
?命令獲取當前日期,格式為?yyyymmdd
- 定義歸檔文件的名稱和保存路徑
- 使用?
tar
?命令進行歸檔壓縮 - 根據?
tar
?命令的執行結果(通過?$?
?判斷),提示歸檔成功或失敗
11.2 向用戶發送消息
該腳本的功能是向某個用戶快速發送消息,需要檢測用戶是否登錄在系統中、是否打開了消息功能,以及當前發送的消息是否為空。
利用 Linux 自帶的?mesg
?和?write
?工具來發送消息。
腳本內容如下:
#!/bin/bash# 檢查用戶是否登錄
login_user=$(who | grep -i -m 1 $1 | awk '{print $1}')
if [ -z $login_user ]
thenecho "$1 不在線!"echo "腳本退出.."exit
fi# 檢查用戶是否打開消息功能
is_allowed=$(who -T | grep -i -m 1 $1 | awk '{print $2}')
if [ $is_allowed != "+" ]
thenecho "$1 沒有開啟消息功能"echo "腳本退出.."exit
fi# 檢查消息是否為空
if [ -z $2 ]
thenecho "沒有消息發出"echo "腳本退出.."exit
fi# 提取消息內容和用戶終端
whole_msg=$(echo $* | cut -d " " -f 2-)
user_terminal=$(who | grep -i -m 1 $1 | awk '{print $2}')# 發送消息
echo $whole_msg | write $login_user $user_terminalif [ $? != 0 ]
thenecho "發送失敗!"
elseecho "發送成功!"
fiexit
腳本說明:
- 使用?
who
?命令結合?grep
?和?awk
?檢查指定的用戶是否登錄,如果用戶不在線,提示錯誤并退出 - 使用?
who -T
?命令檢查用戶是否打開了消息功能(+
?表示打開,-
?表示關閉),如果沒有打開,提示錯誤并退出 - 檢查消息內容是否為空,如果為空,提示錯誤并退出
- 提取消息內容(去掉第一個參數,即用戶名)和用戶登錄的終端
- 使用?
write
?命令向用戶發送消息 - 根據?
write
?命令的執行結果,提示發送成功或失敗
? 總結與建議
通過本文的學習,你已經掌握了 Shell 的基本語法和常用工具的使用,包括:
分類 | 技能點 |
---|---|
腳本基礎 | shebang、執行方式、權限 |
變量 | 系統變量、自定義、只讀 |
控制結構 | if、case、for、while |
輸入輸出 | read、echo |
運算與判斷 | 算術、文件、邏輯 |
正則表達式 | ^ $ . * [] \ |
文本處理 | cut、awk |
函數 | 自定義函數與系統函數 |
實戰案例 | 歸檔、消息發送 |