PowerShell的UTF-8输出

我正在尝试使用带有redirectI / O的Process.Start来使用string调用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

在任何情况下,当我检查给出的字节时,我得到不同的值到我原来的string。 我真的很喜欢解释为什么这不起作用。

这是我的代码:

 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); } } 

更新

我发现如果我做这个脚本:

 [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 

这向我build议我的redirect代码工作正常; 当我在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 

李的回答是对的。 正如Lee所说,我正在试图强制PowerShell生成UTF-8,但这似乎是不可能的。 相反,我们只需要使用PowerShell使用的相同编码(默认OEM编码)读取stream。 没有必要告诉Process.StartInfo使用不同的编码进行读取,因为它已经读取了默认值。

再次更新

其实这是不正确的。 我认为Process.Start使用任何当前的编码; 当我在控制台应用程序下运行它时,它使用了OEM编码,因此可以读取输出。 但是,在Windows服务下运行时,却没有。 所以我必须明确强制。

你可以通过链接@andyb发布控制台获取代码页:

http://blogs.msdn.com/b/ddietric/archive/2010/11/08/decoding-standard-output-and-standard-error-when-redirecting-to-a-gui-application.aspx

我需要在这里使用签名: http : //www.pinvoke.net/default.aspx/kernel32.getcpinfoex

然后分配它:

 CPINFOEX info; if (GetCPInfoEx(CP_OEMCP, 0, out info)) { var oemEncoding = Encoding.GetEncoding(info.CodePage); process.StartInfo.StandardOutputEncoding = oemEncoding; } 

这是.NET中的一个错误。 当PowerShell启动时,它caching输出句柄(Console.Out)。 该文本编写器的Encoding属性不会提取StandardOutputEncoding属性的值。

当您在PowerShell中对其进行更改时,caching的输出写入器的Encoding属性将返回caching的值,因此输出仍使用默认编码进行编码。

作为解决方法,我build议不要更改编码。 它将以Unicodestring的forms返回给您,此时您可以自己pipe理编码。

caching示例:

 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 

不是编码方面的专家,但阅读完这些后…

…这似乎很清楚,$ OutputEncodingvariables只影响传送到本地应用程序的数据。

如果使用PowerShell发送到文件,编码可以由out-file cmdlet上的-encoding参数控制,例如

写输出“hello”|  out-file“enctest.txt”-encoding utf8

在PowerShell方面,你可以做任何事情,但是下面的文章可能会帮助你:

[Console]::OuputEncoding为编码,然后用[Console]::WriteLine打印出来。

如果powershell输出方法有问题,那就不要使用它。 这感觉有点不好,但作品像魅力:)