對于剛接觸 Linux 或 Unix 系統的開發者來說,Shell 腳本往往是自動化操作的第一道門檻。它不像 Python 那樣語法簡潔,也不像 Java 那樣有完善的面向對象體系,但卻能以極少的代碼實現強大的系統管理功能。本文將從 Shell 的基本概念講起,帶你一步步掌握腳本編寫的核心基礎,完成從 “手動操作” 到 “自動化腳本” 的跨越。
一、認識 Shell:什么是 Shell,為什么需要它?
簡單來說,Shell 是用戶與操作系統內核之間的 “翻譯官”—— 它接收用戶輸入的命令,傳遞給內核執行,并將結果返回給用戶。我們在終端中輸入的ls、cd、mkdir等命令,都是通過 Shell 解析執行的。
常見的 Shell 類型
不同的操作系統默認搭載的 Shell 可能不同,常見的有:
- bash:Linux 系統中最常用的 Shell,兼容 sh,支持豐富的擴展功能(如命令補全、歷史記錄);
- zsh:功能更強大的 Shell,支持更多主題和插件,是很多開發者的個性化選擇;
- sh:早期的 Bourne Shell,語法簡單但功能有限,現在多被 bash 兼容替代。
在終端中輸入echo $SHELL可以查看當前使用的 Shell,本文將以最通用的bash為例進行講解。
為什么要學 Shell 編程?
想象以下場景:
- 每天需要手動備份 10 個目錄的日志文件;
- 批量修改 100 個文件的命名格式;
- 定期檢查服務器的磁盤使用率并發送告警。
這些重復勞動如果用 Shell 腳本實現自動化,能節省大量時間。Shell 腳本的核心價值在于 “批量執行命令 + 邏輯控制”,它能將一系列命令按順序組織,并通過條件判斷、循環等邏輯實現復雜任務。
二、第一個 Shell 腳本:Hello World 的誕生
編寫 Shell 腳本的流程非常簡單,只需三個步驟:創建文件→編寫代碼→賦予權限并執行。
1. 創建腳本文件
用任意文本編輯器(如 vim、nano)創建一個.sh后綴的文件,例如hello.sh:
vim hello.sh
2. 編寫腳本內容
在文件中輸入以下代碼:
#!/bin/bash# 這是我的第一個Shell腳本echo "Hello, Shell Programming!"
代碼解析:
- #!/bin/bash:稱為 “shebang”,必須放在腳本第一行,指定腳本由 bash 解釋執行;
- # 這是注釋:#開頭的行是注釋,用于說明代碼功能,不被執行;
- echo "...":打印字符串到終端,類似其他語言的print函數。
3. 賦予執行權限并運行
剛創建的腳本默認沒有執行權限,需要用chmod命令賦予:
chmod +x hello.sh # 賦予執行權限
然后執行腳本:
./hello.sh # 輸出:Hello, Shell Programming!
也可以直接通過 bash 命令運行(無需執行權限):
bash hello.sh # 同樣生效
小技巧:如果腳本放在/usr/local/bin等環境變量目錄下,可直接輸入文件名執行(如hello.sh),無需帶路徑。
三、Shell 變量:存儲數據的容器
和其他編程語言一樣,Shell 也需要變量來存儲數據。但與 Python 等語言不同,Shell 變量的定義和使用有其獨特性。
1. 變量的定義與賦值
定義變量的格式為變量名=值,注意等號兩邊不能有空格:
name="Alice" # 正確:字符串賦值age=25 # 正確:數字賦值# 錯誤寫法:name = "Bob"(等號兩邊有空格)
2. 變量的使用
使用變量時需在變量名前加$符號,例如:
#!/bin/bashname="Alice"echo "My name is $name" # 輸出:My name is Aliceecho "Age: $age" # 輸出:Age: (未定義的變量會被視為空)
如果變量名與其他字符相鄰,可用大括號{}區分:
echo "Hello, ${name}123" # 輸出:Hello, Alice123(若不加{}會被解析為$name123)
3. 環境變量與局部變量
- 局部變量:僅在當前腳本或終端中有效,如上面定義的name;
- 環境變量:全局生效,可被所有子進程訪問,常用的有:
-
- $HOME:用戶主目錄(如/home/alice);
-
- $PATH:命令搜索路徑(存放可執行命令的目錄);
-
- $USER:當前登錄用戶名;
-
- $?:上一條命令的退出狀態(0 表示成功,非 0 表示失敗)。
查看環境變量可用echo,例如:
echo $HOME # 輸出:/home/aliceecho $PATH # 輸出:/usr/local/sbin:/usr/local/bin:...
自定義環境變量需用export命令:
export WORK_DIR="/data/project" # 定義環境變量
四、數據類型:字符串與數字的處理
Shell 是弱類型語言,變量默認都按字符串處理,但也支持數字運算。
1. 字符串操作
字符串是 Shell 中最常用的數據類型,支持拼接、截取、替換等操作。
(1)字符串拼接
直接將變量或字符串放在一起即可:
first="Hello"last="World"full="$first $last" # 拼接為"Hello World"echo $full # 輸出:Hello World
(2)字符串長度
用${#變量名}獲取長度:
str="Shell"echo ${#str} # 輸出:5("Shell"有5個字符)
(3)字符串截取
格式:${變量名:起始位置:長度}(起始位置從 0 開始):
path="/home/user/docs/file.txt"# 從第6個字符開始截取(跳過"/home/")echo ${path:6} # 輸出:user/docs/file.txt# 從第6個字符開始,截取4個字符echo ${path:6:4} # 輸出:user
(4)字符串替換
- 替換第一個匹配項:${變量名/舊字符串/新字符串}
- 替換所有匹配項:${變量名//舊字符串/新字符串}
示例:
filename="report_2023_v1.txt"# 替換第一個"_"為"-"echo ${filename/_/-} # 輸出:report-2023_v1.txt# 替換所有"_"為"-"echo ${filename//_/-} # 輸出:report-2023-v1.txt
2. 數字運算
Shell 默認將數字視為字符串,需用特殊語法進行運算,常用的有兩種方式:
(1)$((表達式))
適合簡單運算:
a=10b=3echo $((a + b)) # 輸出:13echo $((a * b)) # 輸出:30echo $((a / b)) # 輸出:3(整數除法,向下取整)echo $((a % b)) # 輸出:1(取余數)
(2)expr 命令
注意表達式中運算符兩邊需有空格:
expr 10 + 3 # 輸出:13expr 10 \* 3 # 乘法需加轉義符\(避免被Shell解析)
推薦使用$((...)),語法更簡潔且支持變量直接參與運算。
五、輸入輸出與管道:數據的流轉
Shell 腳本的輸入輸出控制和管道操作,是實現命令協作的核心。
1. 標準輸入輸出
默認情況下:
- 標準輸入(stdin):從鍵盤接收輸入,文件描述符為 0;
- 標準輸出(stdout):輸出到終端,文件描述符為 1;
- 標準錯誤(stderr):錯誤信息輸出到終端,文件描述符為 2。
2. 重定向:改變輸入輸出的方向
通過重定向符號可將輸入輸出指向文件:
- >:覆蓋寫入文件(如echo "test" > file.txt);
- >>:追加寫入文件(如echo "test" >> file.txt);
- <:從文件讀取輸入(如read var < file.txt);
- 2>:將錯誤信息寫入文件(如ls error_dir 2> err.log);
- &>:將標準輸出和錯誤都寫入文件(如command &> output.log)。
示例:
# 將命令輸出寫入文件(覆蓋原有內容)ls -l > file_list.txt# 追加輸出到文件echo "新內容" >> file_list.txt# 捕獲錯誤信息rm non_exist_file 2> error.log
3. 管道:命令間的數據傳遞
管道符號|可將前一個命令的輸出作為后一個命令的輸入,實現命令協作:
# 查找包含"error"的日志行,并統計數量cat app.log | grep "error" | wc -l# 列出當前目錄文件,按大小排序(逆序)ls -l | sort -k5,5nr
命令解析:
- grep "error":篩選包含指定字符串的行;
- wc -l:統計行數;
- sort -k5,5nr:按第 5 列(文件大小)逆序(nr)排序。
六、實戰案例:批量文件重命名腳本
掌握了基礎語法后,我們來編寫一個實用腳本:將指定目錄下的所有.txt文件添加前綴(如2023_)。
腳本代碼
?#!/bin/bash# 批量給txt文件添加前綴# 使用方法:./rename_txt.sh 目錄路徑 前綴# 檢查參數是否正確if [ $# -ne 2 ]; thenecho "使用錯誤!正確用法:$0 目錄路徑 前綴"exit 1fidir=$1prefix=$2# 檢查目錄是否存在if [ ! -d "$dir" ]; thenecho "錯誤:目錄 $dir 不存在!"exit 1fi# 切換到目標目錄cd "$dir" || exit 1# 遍歷txt文件并添加前綴for file in *.txt; do# 跳過非文件(如目錄)if [ -f "$file" ]; thennew_name="${prefix}${file}"mv "$file" "$new_name"echo "已重命名:$file → $new_name"fidoneecho "批量重命名完成!"?
代碼解析
- $#表示參數個數,$0表示腳本名,$1、$2表示第一個和第二個參數;
- [ -d "$dir" ]判斷是否為目錄,[ -f "$file" ]判斷是否為文件(條件判斷語法將在后續文章詳細講解);
- for file in *.txt遍歷所有 txt 文件,mv命令執行重命名。
運行效果
# 創建測試文件mkdir test_dirtouch test_dir/{a,b,c}.txt# 執行腳本./rename_txt.sh test_dir 2023_# 輸出結果已重命名:a.txt → 2023_a.txt已重命名:b.txt → 2023_b.txt已重命名:c.txt → 2023_c.txt
批量重命名完成!
七、總結
- Shell 的作用與常見類型;
- 腳本的創建、執行流程;
- 變量定義、數據類型(字符串、數字);
- 輸入輸出重定向與管道操作;
- 一個實戰案例(批量重命名文件)。