Вывод 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:UsersPaulDesktopFoo.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, как и ожидал.

это означает, что В 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

4 ответов


это ошибка .Сеть. При запуске PowerShell кэширует дескриптор вывода (консоль.Из.) Свойство 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

не специалист по кодированию, но после прочтения этих...

... кажется довольно ясным, что переменная $OutputEncoding только влияет на данные, передаваемые в собственные приложения.

если отправка в файл из Withing PowerShell, кодировка может управляться на например

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

ничего другого вы не можете сделать на фронте PowerShell, но следующий пост может помочь вы.:


установить [Console]::OuputEncoding как кодирование, что вы хотите, и распечатать с [Console]::WriteLine.

Если метод вывода powershell имеет проблему, не используйте его. Он чувствует себя немного плохо, но работает как шарм :)


провел некоторое время, работая над решением моей проблемы, и подумал, что это может быть интересно. Я столкнулся с проблемой, пытаясь автоматизировать генерацию кода с помощью PowerShell 3.0 В Windows 8. Целевой IDE был компилятором Keil с использованием MDK-ARM Essential Toolchain 5.24.1. Немного отличается от OP, так как я использую PowerShell изначально на этапе предварительной сборки. Когда я попытался #включить сгенерированный файл, я получил ошибку

неустранимая ошибка: обнаружена метка порядка байтов UTF-16 (LE) '..\ GITVersion.h' но кодировка не поддерживается

Я решил проблему, изменив строку, которая сгенерировала выходной файл из:

out-file -FilePath GITVersion.h -InputObject $result

в:

out-file -FilePath GITVersion.h -Encoding ascii -InputObject $result