身分證影本加浮水印文章接到讀者回報奇怪錯誤,說 $resized.Save([IO.Path]::ChangeExtension($imgPath, "Watermark.jpg"), [System.Drawing.Imaging.ImageFormat]::Jpeg) 這段會出現「字串遺漏結尾字元: "」錯誤,將字串改成 'Watermark.jpg' (雙引號改單引號) 可以解決,問 PowerShell 是否需要特別設定?

這個現象違反我的認知。依我的理解,字串值使用雙引號跟單引號的差別在於:雙引號時會解析替換掉其中包含的 $varName 變數或 $(expression) 運算式,而單引號不會,字串內容不做任何解析替換。故我推測遺漏結尾字元可能是輸入時字元符號有誤引起,應屬偶發個案。

但是,當第二位讀者也回報相同狀況,讓我意識到案情不單純,也馬上想到:啊! 該不會是 UTF-8 編碼問題?

果然,刻意將 .ps1 檔存成 UTF-8 (正確做法應該要存 UTF-8 with BOM),我也能重現同樣錯誤!

這是個已知問題 - VSCode PowerShell 中文編碼問題,PowerShell 5.x 在中文版作業系統預設會用 BIG5 編碼解析 .ps1 檔案,當其中包含中文就可能解析錯誤。(PS 6.0 起預設編碼改用 UTF-8,較無此問題)

而解析錯誤導致錯誤有很多重樣貌,大部分情境會在錯誤訊息看到亂碼,我們很快意識到是中文編碼問題;但如果運氣不好,風馬牛不相及的錯誤訊息會讓人鬼打牆很久,才聯想到跟中文編碼造成的,像是本次的案例。

我們將 "Watermark.jpg" 換成 'Watermark.jpg',一樣會出現「字串遺漏結尾字元: "」錯誤,但錯誤程式位置會有中文亂碼:

由亂碼樣式,我們可以推敲錯在 "Watermark.jpg" 那行缺結尾字元的原因 - "微軟正黑體" 被解析成 "敺株?甇??擃? ,結尾的 " 被吃掉了,於是 PowerShell 一路向下尋找 ",直到遇到 "Watermark.jpg" ,拿開頭的 " 當成 "敺株?甇??擃? 的結尾字元,而 .jpg 後方的 " 成為另一個沒有結尾的字串起首,這就是上面中文變亂碼,但錯誤爆在 Watermark.jpg 的原因。

同場加映:使用中文編碼解析工具驗證「微軟正黑體」如何變「敺株?甇??擃?」

示範「微軟正黑體」如何變「敺株?甇??擃?」

掌握這個原理,我們不難想像:依據中文字元及相鄰程式碼不同,也可能出現「字串遺漏結尾字元: "」以外的錯誤,甚至不會出錯。例如:


Font 建構參數有錯


字體名稱字串如寫新細明體則不會出錯

總之,在 PowerShell 5.x,若 .ps1 裡有寫中文,檔案請記得要存成 UTF-8 with BOM 編碼,否則可能會遇到一堆奇怪錯誤。運氣好的話在錯誤訊息看到亂碼很快理解到跟中文編碼有關,有時錯誤會以詭異形態出現,甚至沒有錯誤訊息,只是結果不符預期,很難想到跟編碼有關,要查很久。

而「確認檔案編碼」也能列為 .ps1 問題調查 SOP 之必要項目。

Case of .ps1 without UTF-8 BOM causing weird error messages.


Comments

Be the first to post a comment

Post a comment