log - powershell format table convertto html




PowerShell的UTF-8輸出 (3)

我正在嘗試使用帶有重定向I / O的Process.Start用字符串調用PowerShell.exe ,並以UTF-8返回輸出。 但我似乎無法做到這一點。

我嘗試過的:

  • 傳遞命令以通過-Command參數運行
  • 使用UTF-8編碼將PowerShell腳本作為文件寫入磁盤
  • 使用具有BOM編碼的UTF-8將PowerShell腳本作為文件寫入磁盤
  • 使用UTF-16將PowerShell腳本作為文件寫入磁盤
  • 在我的控制台應用程序和PowerShell腳本中設置Console.OutputEncoding
  • 在PowerShell中設置$OutputEncoding
  • 設置Process.StartInfo.StandardOutputEncoding
  • 使用Encoding.Unicode而不是Encoding.UTF8

在每種情況下,當我檢查我給出的字節時,我會得到與原始字符串不同的值。 我真的很想解釋為什麼這不起作用。

這是我的代碼:

static void Main(string[] args)
{
    DumpBytes("Héllo");

    ExecuteCommand("PowerShell.exe", "-Command \"$OutputEncoding = [System.Text.Encoding]::UTF8 ; Write-Output 'Héllo';\"",
        Environment.CurrentDirectory, DumpBytes, DumpBytes);

    Console.ReadLine();
}

static void DumpBytes(string text)
{
    Console.Write(text + " " + string.Join(",", Encoding.UTF8.GetBytes(text).Select(b => b.ToString("X"))));
    Console.WriteLine();
}

static int ExecuteCommand(string executable, string arguments, string workingDirectory, Action<string> output, Action<string> error)
{
    try
    {
        using (var process = new Process())
        {
            process.StartInfo.FileName = executable;
            process.StartInfo.Arguments = arguments;
            process.StartInfo.WorkingDirectory = workingDirectory;
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.CreateNoWindow = true;
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.RedirectStandardError = true;
            process.StartInfo.StandardOutputEncoding = Encoding.UTF8;
            process.StartInfo.StandardErrorEncoding = Encoding.UTF8;

            using (var outputWaitHandle = new AutoResetEvent(false))
            using (var errorWaitHandle = new AutoResetEvent(false))
            {
                process.OutputDataReceived += (sender, e) =>
                {
                    if (e.Data == null)
                    {
                        outputWaitHandle.Set();
                    }
                    else
                    {
                        output(e.Data);
                    }
                };

                process.ErrorDataReceived += (sender, e) =>
                {
                    if (e.Data == null)
                    {
                        errorWaitHandle.Set();
                    }
                    else
                    {
                        error(e.Data);
                    }
                };

                process.Start();

                process.BeginOutputReadLine();
                process.BeginErrorReadLine();

                process.WaitForExit();
                outputWaitHandle.WaitOne();
                errorWaitHandle.WaitOne();

                return process.ExitCode;
            }
        }
    }
    catch (Exception ex)
    {
        throw new Exception(string.Format("Error when attempting to execute {0}: {1}", executable, ex.Message),
            ex);
    }
}

更新1

我發現如果我製作這個腳本:

[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
Write-Host "Héllo!"
[Console]::WriteLine("Héllo")

然後通過以下方式調用它:

ExecuteCommand("PowerShell.exe", "-File C:\\Users\\Paul\\Desktop\\Foo.ps1",
  Environment.CurrentDirectory, DumpBytes, DumpBytes);

第一行已損壞,但第二行不是:

H?llo! 48,EF,BF,BD,6C,6C,6F,21
Héllo 48,C3,A9,6C,6C,6F

這告訴我,我的重定向代碼一切正常; 當我在PowerShell中使用Console.WriteLine ,我得到了我期望的UTF-8。

這意味著PowerShell的Write-OutputWrite-Host命令必須對輸出執行不同的操作,而不是簡單地調用Console.WriteLine

更新2

我甚至嘗試過以下操作來強制PowerShell控制台代碼頁為UTF-8,但是當[Console]::WriteLine工作時, Write-HostWrite-Output繼續產生損壞的結果。

$sig = @'
[DllImport("kernel32.dll")]
public static extern bool SetConsoleCP(uint wCodePageID);

[DllImport("kernel32.dll")]
public static extern bool SetConsoleOutputCP(uint wCodePageID);
'@

$type = Add-Type -MemberDefinition $sig -Name Win32Utils -Namespace Foo -PassThru

$type::SetConsoleCP(65001)
$type::SetConsoleOutputCP(65001)

Write-Host "Héllo!"

& chcp    # Tells us 65001 (UTF-8) is being used

不是編碼方面的專家,但在閱讀完這些...

...似乎很清楚$ OutputEncoding變量只影響傳輸到本機應用程序的數據。

如果使用PowerShell發送到文件,則可以通過out-file cmdlet上的-encoding參數控制-encoding ,例如

write-output "hello" | out-file "enctest.txt" -encoding utf8

那麼你無法在PowerShell前面做任何其他事情,但以下帖子可能對你有所幫助:


[Console]::OuputEncoding為您想要的任何編碼,並使用[Console]::WriteLine打印出來。

如果powershell輸出方法有問題,那麼不要使用它。 感覺有點不好,但是像魅力一樣:)


這是.NET中的一個錯誤。 當PowerShell啟動時,它會緩存輸出句柄(Console.Out)。 該文本編寫器的Encoding屬性不會獲取值StandardOutputEncoding屬性。

從PowerShell中更改它時,緩存的輸出編寫器的Encoding屬性返回緩存的值,因此輸出仍使用默認編碼進行編碼。

作為一種解決方法,我建議不要改變編碼。 它將作為Unicode字符串返回給您,此時您可以自己管理編碼。

緩存示例:

102 [C:\Users\leeholm]
>> $r1 = [Console]::Out

103 [C:\Users\leeholm]
>> $r1

Encoding                                          FormatProvider
--------                                          --------------
System.Text.SBCSCodePageEncoding                  en-US



104 [C:\Users\leeholm]
>> [Console]::OutputEncoding = [System.Text.Encoding]::UTF8

105 [C:\Users\leeholm]
>> $r1

Encoding                                          FormatProvider
--------                                          --------------
System.Text.SBCSCodePageEncoding                  en-US






io-redirection