批量重命名是運維與開發高頻剛需:統一副檔名、大小寫轉換、前綴/後綴增刪、連續編號、正則捕獲組重組……本文匯總 rename(Perl 版)、Bash for 迴圈、sed/awk 管線、find -exec 等多種技術路線,覆蓋 12 大實戰場景,附帶效能對比與避坑指南,讓你告別手工 mv 的低效與風險。
現代 Linux 發行版預裝 rename(prename,Perl 實現),支援完整 Perl 正則與捕獲組,語法:
rename 's/模式/替換/' 檔案列表
rename 'y/搜尋字元/替換字元/' 檔案列表
# 移除所有 .bak 結尾
rename 's/\.bak$//' *.bak
# .jpe → .jpg
rename 's/\.jpe$/.jpg/' *.jpe
rename 'y/A-Z/a-z/' *
核心語法:for f in 模式; do mv "$f" "${f/舊/新}"; done,利用 ${var%模式}(尾部刪除)、${var#模式}(首部刪除)、${var/舊/新}(替換)。
# abcd.jpg → abcd_efg.jpg
for f in *.jpg; do mv "$f" "${f%.jpg}_efg.jpg"; done
# abcd_efg.jpg → abcd_lmn.jpg
for f in *.jpg; do mv "$f" "${f%_efg.jpg}_lmn.jpg"; done
for f in *; do mv -f "$f" "$(echo "$f" | tr a-z A-Z)"; done
# a_1.jpg → a_01.jpg
for f in *_?.jpg; do
mv "$f" "$(echo "$f" | awk -F_ '{print $1"_0"$2}')"
done
# 前 3 字元替換為 vzomik
for f in *; do mv -f "$f" "$(echo "$f" | sed 's/^.../vzomik/')"; done
# 後 4 字元替換為 vzomik
for f in *; do mv -f "$f" "$(echo "$f" | sed 's/....$/vzomik/')"; done
| 需求 | 寫法 | 說明 |
|---|---|---|
| 數字範圍 | for i in {1..100} | Brace 展開,Bash 3.0+ |
| 數字範圍(大數) | for ((i=1;i<=1000000;i++)) | C 風格,無 1e+06 科學記號問題 |
| seq 輸出 | for i in $(seq 1 100) | 相容性好,大數會產生科學記號 |
| 陣列遍歷 | for i in "${arr[@]}" | 雙引號保留空白字元 |
| 位置參數 | for i in "$@" | 腳本參數遍歷 |
| Glob 擴展 | for f in *.txt | 直接匹配,無需 ls |
| 命令輸出 | for i in $(cmd) | 注意換行/空白切分陷阱 |
| 多目錄遍歷 | for d in $LIST | LIST="a b c" 空白分隔 |
| awk 內建迴圈 | awk 'BEGIN{for(i=1;i<=10;i++)print i}' | 純數值運算最快 |
測試環境:Bash 5.1,迴圈 1,000,000 次空 echo:
| 寫法 | 耗時 | 備註 |
|---|---|---|
for ((i=1;i<=1000000;i++)) | ~0.8s | 最快,純整數運算 |
for i in {1..1000000} | ~1.2s | Brace 展開記憶體佔用大 |
for i in $(seq 1 1000000) | ~2.5s | 子命令開銷,>1e6 產生科學記號 |
while ((i++<1000000)) | ~1.0s | 次之 |
結論:百萬級以上迴圈首選 C 風格 for ((...));需字串列表時用 while IFS= read -r 讀取檔案/命令輸出,避免 for i in $(cmd) 切分錯誤。
#!/bin/bash
# rename_tool.sh [-n] [-r] 's/from/to/' files...
# -n: dry-run 只顯示不執行
# -r: 遞迴處理子目錄
dry_run=false
recursive=false
while getopts "nr" opt; do
case $opt in
n) dry_run=true ;;
r) recursive=true ;;
esac
done
shift $((OPTIND-1))
pattern=$1
shift
shopt -s nullglob globstar
if $recursive; then
files=(**/*)
else
files=("$@")
fi
for f in "${files[@]}"; do
[[ -e $f ]] || continue
new=$(echo "$f" | sed -E "$pattern")
[[ $f == $new ]] && continue
if $dry_run; then
echo "Would rename: $f → $new"
else
mv -v "$f" "$new"
fi
done
用法:
./rename_tool.sh -n 's/_v[0-9]+//' *.pdf # 預覽移除 _v數字
./rename_tool.sh -r 's/ /_/g' # 遞迴替換空格為底線
rename 's///' 一行搞定,可讀性最強。for + 參數擴展/外部命令管線。find . -type f -name '*.jpg' -exec bash -c '...' {} + 避免參數列表過長。-n (dry-run):先預覽,確認無誤再執行。cp -a target_dir target_dir.bak,重命名不可逆。掌握這套「rename 為主、for 迴圈為輔、find -exec 為底」的組合拳,任何批量重命名需求都能在分鐘級完成,且零失誤。