UTF-8 вывод из PowerShell
Я пытаюсь использоватьProcess.Start
с перенаправленным вводом / выводом для вызоваPowerShell.exe
со строкой, и чтобы вернуть вывод, все вUTF-8,, Но я, похоже, не смогу сделать эту работу.
Что я пробовал:
Передача команды для запуска через-Command
параметрЗапись сценария PowerShell в виде файла на диск с кодировкой UTF-8Запись сценария PowerShell в виде файла на диск с помощью UTF-8 сBOM кодированиеЗапись сценария PowerShell в виде файла на диск с помощью UTF-16настройкаConsole.OutputEncoding
как в моем консольном приложении, так и в скрипте PowerShellнастройка$OutputEncoding
в PowerShellнастройка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
Это говорит о том, что мой код перенаправления работает нормально; когда я используюConsole.WriteLine
в PowerShell я получаю UTF-8, как я и ожидал.
Это означает, что PowerShellWrite-Output
а такжеWrite-Host
команды должны делать что-то другое с выводом, а не просто вызыватьConsole.WriteLine
.
Я даже попробовал следующее, чтобы заставить кодовую страницу консоли 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