Linux Wildcard 萬用字元完整教學:*、?、[] 通配符用法
在 Linux 命令列中,**Wildcard(萬用字元/通配符)**是批次操作檔案的強大工具。學會
*、?、[]通配符以及 Brace Expansion,就能用一行指令完成原本需要幾十行才能做到的批次操作,大幅提升工作效率。
什麼是 Wildcard?
Wildcard(萬用字元,也稱通配符)是 Shell 在解析指令時,用來匹配多個檔案名稱的特殊字符。當你輸入 ls *.txt 時,Shell 會先把 *.txt 展開成目前目錄中所有符合條件的檔案名稱,再把完整的檔案清單傳給 ls 指令。
這個展開過程叫做 Glob 展開(Globbing),是 Shell 的功能,不是 ls、cp、rm 這些指令自己的功能。這一點很重要:Wildcard 的展開是在指令執行之前就發生的。
Wildcard 的主要用途:
- 批次複製、移動、刪除符合特定命名規則的檔案
- 只列出特定副檔名的檔案
- 快速選取一系列編號的檔案
- 搭配
find、grep等指令進行進階搜尋
Wildcard 完整語法表
| 通配符 | 名稱 | 說明 | 範例 | 匹配結果 |
|---|---|---|---|---|
* | 星號 | 匹配任意長度的任意字符(包含零個字符) | *.txt | a.txt, readme.txt, hello.txt |
? | 問號 | 匹配恰好一個任意字符 | file?.txt | file1.txt, fileA.txt(不匹配 file10.txt) |
[abc] | 字符集合 | 匹配括號內的任意一個字符 | file[123].txt | file1.txt, file2.txt, file3.txt |
[a-z] | 字符範圍 | 匹配指定範圍內的任意一個字符 | file[a-z].txt | filea.txt, fileb.txt, …filez.txt |
[!abc] | 排除字符集合 | 匹配不在括號內的任意一個字符 | file[!0-9].txt | filea.txt, fileb.txt(不匹配 file1.txt) |
基本 Wildcard 用法
* 星號 — 匹配任意字符
# 列出目前目錄中所有的文字檔
ls *.txt
# 列出所有 .jpg 和 .png 圖片
ls *.jpg
ls *.png
# 列出所有以 "report" 開頭的檔案(不限副檔名)
ls report*
# 將所有 JPEG 圖片複製到指定目錄
cp *.jpg /home/benz/backup/images/
# 將所有 PNG 圖片移動到備份目錄
mv *.png /home/benz/backup/
# 刪除所有臨時 .tmp 檔案(操作前先用 ls 確認要刪的檔案)
ls *.tmp
rm *.tmp
? 問號 — 匹配單一字符
問號只匹配「恰好一個」字符,非常適合處理有固定命名格式的檔案:
# 刪除 file1.txt, file2.txt, fileA.txt 等(不匹配 file10.txt)
rm file?.txt
# 列出所有兩位數日誌(log01.txt 到 log99.txt)
ls log??.txt
# 匹配 data_a.csv, data_b.csv... 但不匹配 data_ab.csv
ls data_?.csv
[] 字符集合 — 精確匹配字符範圍
# 列出 file1.txt、file2.txt、file3.txt(不包含其他數字)
ls file[1-3].txt
# 列出 fileA.txt、fileB.txt、fileC.txt(只有大寫 A、B、C)
ls file[ABC].txt
# 列出名稱以小寫字母開頭的 .sh 腳本
ls [a-z]*.sh
# 刪除只含數字編號的備份(bak1, bak2, bak3)
rm bak[0-9]
# 排除數字,只列出字母結尾的檔案
ls file[!0-9].txt
Brace Expansion(大括號展開)
Brace Expansion 是 Bash 的一個功能,與 Wildcard 相關但略有不同——Wildcard 是「比對已存在的檔案」,而 Brace Expansion 是「產生字符序列」,可以用來批次建立檔案、目錄,或快速印出序列。
數字序列
# 印出 1 到 100
echo {1..100}
# 印出奇數(間隔 2)
echo {1..100..2}
# 印出偶數
echo {2..100..2}
# 每 10 個印一次
echo {10..100..10}
字母序列
# 印出 a 到 z
echo {a..z}
# 反向印出 z 到 a
echo {z..a}
# 間隔 2 個字母
echo {a..z..2}
批次建立目錄和檔案
這是 Brace Expansion 最實用的場景之一:
# 一次建立多層專案目錄結構
mkdir -p project/{src,tests,docs}
# 等同於:
# mkdir -p project/src
# mkdir -p project/tests
# mkdir -p project/docs
# 一次建立 10 個測試檔案
touch file{1..10}.txt
# 建立:file1.txt file2.txt ... file10.txt
# 建立多個月份目錄
mkdir -p logs/{2024-01,2024-02,2024-03,2024-04}
# 快速備份(重新命名)一個檔案
mv config.txt config.txt.bak
# 更簡潔的寫法:
mv config.{txt,txt.bak}
組合使用
# 建立完整的前端專案結構
mkdir -p myapp/{public/{css,js,img},src/{components,pages,utils},tests}
# 批次建立有月份前綴的檔案
touch report_{jan,feb,mar,apr,may,jun}.csv
# 同時建立多種副檔名的測試檔
touch test.{py,js,rb,go}
實用範例集
批次刪除
# 刪除所有 .tmp 暫存檔(先用 ls 確認再刪)
ls *.tmp
rm *.tmp
# 刪除超過 30 天的 .log 檔案
find /var/log -name "*.log" -mtime +30 -delete
# 刪除所有空目錄
find . -type d -empty -delete
批次複製(含篩選條件)
# 只複製圖片檔到備份目錄
cp *.{jpg,jpeg,png,gif} /home/benz/backup/images/
# 複製所有以日期命名的報表(格式如 report_2024-01-01.csv)
cp report_2024-*.csv /home/benz/archive/
# 把所有腳本複製到另一台機器
scp *.sh user@remote:/home/user/scripts/
搭配 find 指令
# 找出所有 Python 腳本
find . -name "*.py"
# 找出所有日誌並計算總大小
find /var/log -name "*.log" -exec du -sh {} \;
# 找出並壓縮所有超過 7 天的日誌
find /var/log -name "*.log" -mtime +7 -exec gzip {} \;
搭配 grep 指令
# 在所有 .conf 設定檔中搜尋特定字串
grep -r "ServerName" /etc/nginx/*.conf
# 在所有 Python 檔案中搜尋函數定義
grep -rn "def " *.py
# 遞迴搜尋所有子目錄中的 JavaScript 檔案
grep -r "console.log" --include="*.js" .
Wildcard vs 正規表示式
初學者常常混淆 Wildcard 和正規表示式(Regular Expression,RegEx),雖然兩者看起來相似,但用途完全不同:
| 比較項目 | Wildcard(通配符) | 正規表示式(RegEx) |
|---|---|---|
| 用途 | Shell 中匹配檔案名稱 | 匹配文字內容 |
| 使用工具 | ls, cp, mv, rm 等 Shell 指令 | grep, sed, awk, 程式語言 |
* 的含義 | 任意長度任意字符 | 前一個字符出現 0 次或多次 |
? 的含義 | 任意單一字符 | 前一個字符出現 0 次或 1 次 |
. 的含義 | 普通的點(句號) | 匹配任意單一字符 |
[abc] 的含義 | 相同(匹配集合中的一個字符) | 相同 |
語法對比範例:
# Wildcard(Shell Glob)
# 匹配所有 .txt 檔案
ls *.txt
# 正規表示式(grep)
# 匹配包含一個或多個數字的行
grep "[0-9]+" file.txt
# 在 grep 中,如果要用 . 匹配任意字符(RegEx 語義)
grep "file.txt" logs.txt # . 在 RegEx 中是「任意字符」
grep "file\.txt" logs.txt # 要匹配字面上的點,需要跳脫
常見問題(FAQ)
Q1:* 和 ** 有什麼不同?
標準 Bash Wildcard 的 * 不會匹配目錄分隔符號 /,也就是說 *.txt 只會匹配目前目錄下的 .txt 檔,不會遞迴進入子目錄。
**(雙星號)是 Bash 4.0+ 的 globstar 選項啟用後才有的功能,可以遞迴匹配任意深度的目錄:
# 開啟 globstar 選項
shopt -s globstar
# ** 匹配所有子目錄下的 .txt 檔
ls **/*.txt
# 不需要 globstar 的替代方案是使用 find
find . -name "*.txt"
Q2:Wildcard 怎麼匹配檔案名稱中含有空格的檔案?
檔案名稱含有空格時,用一般 Wildcard 可能會產生意外的解析問題。推薦的做法是搭配引號或 find:
# 如果有個檔案叫 "my document.txt"
# 用引號包住 Wildcard(這樣 Shell 就不會展開,通常不是我們要的)
ls "my document.txt"
# 正確做法:用 find 並加上 -print0 / xargs -0 處理空格
find . -name "*.txt" -print0 | xargs -0 ls -l
# 或用 bash 迴圈處理(最安全)
for f in *.txt; do
echo "Processing: $f"
done
# bash 的 for 迴圈可以正確處理有空格的檔名
延伸閱讀: