[AI協作] 使用 Metadata 資料重新命名 MP4 影片檔案:完整腳本與踩坑紀錄
整理歷年影片備份時,發現檔名存在各種格式不一致的問題:有些檔名帶有 6 位數秒級時間,有些帶有 9 位數毫秒級時間,有些說明文字直接黏在數字後面;更棘手的是,從相機或手機匯出的影片,檔名上的時間與 MP4 內部的 Metadata(creation_time)存在 UTC 時區差。
為了批量修正這些影片,我決定寫一個 Bash 腳本來自動化處理。這篇文章紀錄了開發過程中遇到的幾個 Shell 特性陷阱,以及如何透過與AI協作快速疊代出穩定的解決方案。
核心需求與規則設計
在動手寫腳本前,我歸納了幾條明確的改名與正規化規則:
Metadata 時區對齊:調用
ffprobe讀取影片標籤中的creation_time(ISO 8601 UTC),並自動換算為執行主機的本地時區(例如 UTC+8)。時間前綴規格化:
將
8碼_6碼(YYYYMMDD_hhmmss)補充為8碼_9碼(補上000毫秒)。時間前綴與後續說明文字之間強制補上一個空格。
清理字串尾端的無用空格。
安全機制:
預設啟用 Dry-Run 預覽模式,傳入
--rename才會執行mv。針對更名後的檔案進行碰撞檢查(Collision handling),重名時自動追加
_1,_2後綴。
關鍵踩坑與除錯過程(Debugging Highlights)
在開發與測試階段,遇到了幾個非常經典的 Bash 與 Linux CLI 陷阱:
1. GNU date 解析 ISO 8601 時區的「雙重時區疊加」陷阱
一開始使用 date -d "$RAW_TIME UTC +8 hours" 來換算 UTC+8,結果發現 2019-12-19T01:10:23.000000Z 換算出來居然變成了 17:10:23(多了 16 小時)。
原因分析:ISO 字串結尾的
Z代表 UTC,但date在同時讀到Z與後續的UTC字串時,會先將時間誤判為 Local Time 加上 8 小時,最後輸出時系統的時區又再次疊加了 8 小時。解法:改用
TZ環境變數傳遞給date命令,讓其直接讀取 ISO 字串進行時區轉換:
TARGET_TIME=$(date -d "$RAW_TIME" "+%Y-%m-%d %H:%M:%S %z")
2. Bash 正則表達式 (=~) 的 Locale 相容性問題
原本使用 ^[0-9]{8}_[0-9]{6}([0-9]{3})?([^0-9]|$) 來比對檔名,在英文路徑下運作正常,但遇到中文檔名(如 20210123_164704超音波.mp4)時,Bash 的 [^0-9] 條件會在特定系統 Locale 下失靈,導致檔案直接被跳過。
解法:放棄過度複雜的單一 Regex,改用
grep -oE提取前綴並精確比對字串長度(15 碼或 18 碼),大幅提升在各種語系環境下的穩定度:
3. 檔名包含空格與中文的 Shell 斷詞問題
當使用 find 搜尋子目錄檔案時,一般的管道 while read 會因為檔名中的空格而引發斷詞異常。
解法:全面採用
find -print0搭配read -r -d '',改用 Null 字元 (\0) 作為分隔符,確保特殊路徑能被 100% 正確解析。
📸 實際執行結果
🌍 常見旅遊國家與時區對照(-tz 參數設定)
整理海外旅遊影片時,可直接傳入 -tz 參數轉換為拍攝當地的時間:
+8(UTC+8):台灣、中港澳、新加坡、馬來西亞、菲律賓、峇里島+9(UTC+9):日本、韓國+7(UTC+7):泰國、越南、柬埔寨、雅加達
舉例:日本旅遊影片,遞迴掃描子目錄並先預覽
./scan_and_rename_mp4.sh -r -tz +9
舉例:泰國旅遊影片,確認無誤後執行實際改名
./scan_and_rename_mp4.sh -r -tz +7 --rename
🛠️ 完整 Bash 腳本實作
結語
這套腳本是由我提出邊界條件與除錯,並由 AI (Gemini) 協助實作語法結構與邏輯微調的成果。大幅省去了查閱 Man Page 與批次處理檔名的時間,分享給同樣需要整理大量影片的朋友!

留言