wake-up-neo.com

UTF-8-Ausgabe von PowerShell

Ich versuche, Process.Start mit umgeleiteter E/A zu verwenden, um PowerShell.exe mit einer Zeichenfolge aufzurufen und die Ausgabe in UTF-8 zurückzuholen. Aber ich scheine das nicht schaffen zu können.

Was ich probiert habe:

  • Übergeben des Befehls zur Ausführung über den Parameter -Command
  • Schreiben des PowerShell-Skripts als Datei mit UTF-8-Kodierung auf die Festplatte
  • Schreiben des PowerShell-Skripts als Datei mit UTF-8 mit BOM encoding auf Platte
  • Schreiben des PowerShell-Skripts mit UTF-16 als Datei auf die Festplatte
  • Festlegen von Console.OutputEncoding in meiner Konsolenanwendung und im PowerShell-Skript
  • Festlegen von $OutputEncoding in PowerShell
  • Process.StartInfo.StandardOutputEncoding einstellen
  • Alles mit Encoding.Unicode anstelle von Encoding.UTF8

In jedem Fall bekomme ich bei der Überprüfung der angegebenen Bytes andere Werte als meine ursprüngliche Zeichenfolge. Ich würde wirklich eine Erklärung lieben, warum das nicht funktioniert.

Hier ist mein Code:

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

Update 1

Ich fand das, wenn ich dieses Skript mache:

[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
Write-Host "Héllo!"
[Console]::WriteLine("Héllo")

Dann rufen Sie es über auf:

ExecuteCommand("PowerShell.exe", "-File C:\\Users\\Paul\\Desktop\\Foo.ps1",
  Environment.CurrentDirectory, DumpBytes, DumpBytes);

Die erste Zeile ist beschädigt, die zweite jedoch nicht:

H?llo! 48,EF,BF,BD,6C,6C,6F,21
Héllo 48,C3,A9,6C,6C,6F

Dies zeigt mir, dass mein Weiterleitungscode einwandfrei funktioniert. Wenn ich Console.WriteLine in PowerShell verwende, erhalte ich UTF-8 wie erwartet.

Dies bedeutet, dass die Write-Output- und Write-Host-Befehle von PowerShell bei der Ausgabe etwas anderes tun müssen und nicht einfach Console.WriteLine aufgerufen werden.

Update 2

Ich habe sogar Folgendes versucht, um die Codepage der PowerShell-Konsole auf UTF-8 zu setzen, aber Write-Host und Write-Output erzeugen weiterhin fehlerhafte Ergebnisse, während [Console]::WriteLine funktioniert.

$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
41
Paul Stovell

Dies ist ein Fehler in .NET. Beim Start von PowerShell wird das Ausgabehandle (Console.Out) zwischengespeichert. Die Encoding-Eigenschaft dieses Texterstellers übernimmt nicht die Eigenschaft StandardOutputEncoding.

Wenn Sie den Wert von PowerShell aus ändern, gibt die Encoding-Eigenschaft des zwischengespeicherten Ausgabe-Writers den zwischengespeicherten Wert zurück, sodass die Ausgabe weiterhin mit der Standardcodierung codiert wird.

Als Problemumgehung würde ich vorschlagen, die Kodierung nicht zu ändern. Es wird Ihnen als Unicode-String zurückgegeben. Dann können Sie die Kodierung selbst verwalten.

Caching-Beispiel:

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
20
LeeHolmes

Kein Experte für Codierung, aber nachdem ich diese gelesen habe ...

Es scheint ziemlich klar zu sein, dass die Variable $ OutputEncoding nur Daten beeinflusst, die an native Anwendungen weitergeleitet werden. 

Wenn Sie von PowerShell aus an eine Datei senden, kann die Kodierung durch den -encoding-Parameter des out-file-Cmdlets gesteuert werden, z.

 Schreibausgabe "Hallo" | out-file "enctest.txt" -encoding utf8 

Auf der PowerShell-Frontseite können Sie dann nichts anderes tun, aber der folgende Beitrag kann Ihnen dabei helfen:.

19
andyb

Stellen Sie den [Console]::OuputEncoding als Kodierung ein und drucken Sie ihn mit [Console]::WriteLine aus. 

Wenn die PowerShell-Ausgabemethode ein Problem hat, verwenden Sie sie nicht. Es fühlt sich ein bisschen schlecht an, funktioniert aber wie ein Zauber :)

1
jhk

Ich habe einige Zeit an einer Lösung für mein Problem gearbeitet und dachte, es könnte von Interesse sein. Ich hatte ein Problem mit dem Versuch, die Codegenerierung mit PowerShell 3.0 unter Windows 8 zu automatisieren. Das Ziel IDE war der Keil-Compiler mit MDK-ARM Essential Toolchain 5.24.1. Etwas anders als OP, da ich PowerShell nativ während des Vorbereitungsschritts verwende. Als ich versuchte, die generierte Datei # einzuschließen, wurde der Fehler angezeigt 

schwerwiegender Fehler: UTF-16 (LE) -Byteintrittskennzeichen '..\GITVersion.h' Die Kodierung wird jedoch nicht unterstützt

Ich habe das Problem gelöst, indem ich die Zeile geändert habe, aus der die Ausgabedatei generiert wurde:

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

zu:

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