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-Output
和Write-Host
命令必须与输出做一些不同的事情,而不是简单地调用Console.WriteLine
。
更新2
我甚至尝试了以下操作来强制PowerShell控制台代码页为UTF-8,但是当[Console]::WriteLine
工作时, Write-Host
和Write-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
不是编码方面的专家,但阅读完这些后…
- http://blogs.msdn.com/b/powershell/archive/2006/12/11/outputencoding-to-the-rescue.aspx
- http://technet.microsoft.com/en-us/library/hh847796.aspx
- http://www.johndcook.com/blog/2008/08/25/powershell-output-redirection-unicode-or-ascii/
…这似乎很清楚,$ OutputEncodingvariables只影响传送到本地应用程序的数据。
如果使用PowerShell发送到文件,编码可以由out-file
cmdlet上的-encoding参数控制,例如
写输出“hello”| out-file“enctest.txt”-encoding utf8
在PowerShell方面,你可以做任何事情,但是下面的文章可能会帮助你:
将[Console]::OuputEncoding
为编码,然后用[Console]::WriteLine
打印出来。
如果powershell输出方法有问题,那就不要使用它。 这感觉有点不好,但作品像魅力:)