Linux Wildcard 萬用字元完整教學:*、?、[] 通配符用法

2024/01/04

Linux 命令列中,**Wildcard(萬用字元/通配符)**是批次操作檔案的強大工具。學會 *?[] 通配符以及 Brace Expansion,就能用一行指令完成原本需要幾十行才能做到的批次操作,大幅提升工作效率。

什麼是 Wildcard?

Wildcard(萬用字元,也稱通配符)是 Shell 在解析指令時,用來匹配多個檔案名稱的特殊字符。當你輸入 ls *.txt 時,Shell 會先把 *.txt 展開成目前目錄中所有符合條件的檔案名稱,再把完整的檔案清單傳給 ls 指令。

這個展開過程叫做 Glob 展開(Globbing),是 Shell 的功能,不是 lscprm 這些指令自己的功能。這一點很重要:Wildcard 的展開是在指令執行之前就發生的。

Wildcard 的主要用途:

  • 批次複製、移動、刪除符合特定命名規則的檔案
  • 只列出特定副檔名的檔案
  • 快速選取一系列編號的檔案
  • 搭配 findgrep 等指令進行進階搜尋

Wildcard 完整語法表

通配符名稱說明範例匹配結果
*星號匹配任意長度的任意字符(包含零個字符)*.txta.txt, readme.txt, hello.txt
?問號匹配恰好一個任意字符file?.txtfile1.txt, fileA.txt(不匹配 file10.txt
[abc]字符集合匹配括號內的任意一個字符file[123].txtfile1.txt, file2.txt, file3.txt
[a-z]字符範圍匹配指定範圍內的任意一個字符file[a-z].txtfilea.txt, fileb.txt, …filez.txt
[!abc]排除字符集合匹配不在括號內的任意一個字符file[!0-9].txtfilea.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 迴圈可以正確處理有空格的檔名

延伸閱讀: