【发布时间】:2014-04-16 10:43:37
【问题描述】:
我正在尝试使用带有重定向 I/O 的 Process.Start 来调用带有字符串的 PowerShell.exe,并取回输出,所有这些都在 UTF-8 中。但我似乎无法完成这项工作。
我尝试过的:
- 通过
-Command参数传递命令运行 - 使用 UTF-8 编码将 PowerShell 脚本作为文件写入磁盘
- 使用 UTF-8 和 BOM 编码将 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-Output 和 Write-Host 命令必须对输出执行不同的操作,而不是简单地调用 Console.WriteLine。
更新 2
我什至尝试了以下方法来强制 PowerShell 控制台代码页为 UTF-8,但 Write-Host 和 Write-Output 继续产生错误的结果,而 [Console]::WriteLine 有效。
$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
【问题讨论】:
-
为什么从
Powershell.exe开始而不是使用System.Management.Automation到embed PowerShell right in the app? -
我有一个朋友,他在
System.Management.Automation之上构建了一个完整的应用程序。但是在用户第 765 次投诉“我有一个脚本在 PowerShell.exe 下像这样工作,但在你的主机下像这样工作”之后,我,我的意思是我的朋友,决定放弃这个想法。 -
我在这里发布了一些信息:octopusdeploy.com/blog/improving-powershell
-
我认为你想多了......你只需要接受它是 UTF-16 的事实 ;-)
-
为什么说是UTF-16?这似乎是 OEM 代码页
标签: powershell encoding utf-8 character-encoding io-redirection