2023年10月12日 星期四

Bat/PowerShell 中呼叫外部程序 ssh 亂碼 (編碼轉換函式)

Bat/PowerShell 中呼叫外部程序 ssh 亂碼 (編碼轉換函式)

遇到的情況蠻坑人的某些程序預設會強制使用 utf8 ,這會導這不支援的 cmd 直接就攤手出亂碼了

還特別坑的是 ssh 會強制修改 chcp 改成 65001 導致出錯誤信息的時候連帶後面的 echo 都一起跟著亂碼了

想了很多辦法最後試出來的是,委託給powershell執行,雖然還是無法避免被切 chcp 不過可以一起封裝在裡面執行完順便切回來





如何正確的在bat中打印utf8編碼的文字

先說一下最簡情況,我們一步一步來強化,首先就是丟進去執行

powershell -nop "winscp.com /script=script.txtx; exit $lastexitcode"


如此一來就可以得到正確的文字了,順便用 Exit 把執行的返回代碼給帶出來。
此時 PowerShell 會把執行結果回丟到 Cmd 上,可以在透過一般的寫法完整的捕捉到。

powershell -nop "winscp.com /script=script.txtx; exit $lastexitcode" > %LOG% 2>&1


這樣錯誤信息也會一起出來了。
再來下一步我們把重要的指令部分提取出來用管道送進去,剩下不重要的封裝在裡面

echo winscp.com /script=script.txt| powershell -nop "$input|iex; exit $lastexitcode"


這樣就抽取出來了,只需要在意管道的左邊即可,剩下都封裝在右邊
下一步是修正前面提到擅自更改 chcp 的問題

echo winscp.com /script=scripts.txt| powershell -nop "$input|iex; [console]::OutputEncoding=[Text.Encoding]::Default; Exit $LASTEXITCODE"


一樣封裝在 PowerShell 這樣可以眼不見為淨,就此就完成了





如何讓遠端 Linux 程序正確的顯示

兩個方法在 Unix 端轉換後傳輸或是接收到之後再轉換。

這邊範例是用 Windows PowerShell 去獲取 WSL 中的 Big5編碼 的注音文檔案。


方法1 在本地端收到後才轉編碼

先上函式
# 轉換編碼
function Convert-Encoding {
    param(
        [Parameter(Mandatory=$true)]
        [string]$FromEncoding,

        [Parameter(ValueFromPipeline=$true)]
        [string]$InputString
    )

    process {
        $bytes = [System.Text.Encoding]::GetEncoding($FromEncoding).GetBytes($InputString)
        $outputString = [System.Text.Encoding]::GetEncoding($FromEncoding).GetString($bytes)
        $outputString
    }
}

然後是用例

ssh chg@localhost "cat TestFile.txt" | Convert-Encoding -FromEncoding Big5


方法2 在發換端先轉換編碼才發送

ssh chg@localhost "cat TestFile.txt |iconv -f BIG5 -t UTF-8"



兩個方法都可以一定程度處理掉編碼的問題,但這種方式有幾個明顯的問題

  1. 無法轉換 stderr 或是直接打印的
  2. 依賴工具間的編碼適應性 [wsl::shell.sh] -> [Win::ssh.exe] -> [Win::program.ps1]

先說第一個如果這個程序直接丟 print 到視窗這種的會被跳過無法擷取,以及如果是丟到通道2錯誤流信息也是無法獲取。

當然這個無法獲取是目前的代碼無法獲取,對代碼進行擴展還是能處理上述的問題的,只是代碼會變得非常龐大,非常沒必要。


第二個問題就比較棘手了,因為 ssh 在中間層而且新版本強制固定下來就是 utf8 了,在這個轉換過程中是可能會丟東西的 (比如說把一個 big5 檔案用 utf8 開啟然後儲存)

一個例子就是打開一個 big5編碼的程序 裡面有直接打印的代碼。這種耍流氓的真的只能讓一個代理去接住先轉好再丟給ssh不然會出問題的。變成兩端都要有程序轉碼就是了。

這個問題要解決只能根本上的就是直接傳二進制,就不應該通過這個會擅自編碼的 ssh.exe 獲取信息,傳二進制就能把最終編碼權延後到接收端自己處理了。

對我想說的就是,這種極端環境通過終端機是無法解決了,只能自己寫代碼只有函式庫級別的才可以接收二進制。

不過這種情況其實很容易可以避免,就適當的調整一下咩,讓中間層級的 ssh.exe 這個只會utf8的流氓不要編錯碼就好。

如果是執行sh檔案記得注意一下那個檔案本身的編碼會影響到的。




沒有留言:

張貼留言