Sortie UTF-8 de PowerShell

je suis en train d'utiliser Process.Start avec I/O redirigé vers call PowerShell.exe avec une chaîne, et pour obtenir la sortie arrière, le tout dans UTF-8. Mais je ne semble pas être en mesure de faire ce travail.

Ce que j'ai essayé:

  • passer la commande pour exécuter via le -Command paramètre
  • Écrit le script PowerShell en tant que fichier sur le disque avec l'encodage UTF-8
  • Écrit le script PowerShell en tant que fichier sur le disque avec de l'UTF-8 avec BOM encodage
  • Écrit le script PowerShell en tant que fichier sur le disque avec de l'UTF-16
  • Console.OutputEncoding dans mon application console et dans le script PowerShell
  • $OutputEncoding dans PowerShell
  • Process.StartInfo.StandardOutputEncoding
  • le Faire avec Encoding.Unicode au lieu de Encoding.UTF8

dans tous les cas, quand j'inspecte les octets qui m'ont été donnés, j'obtiens des valeurs différentes de ma chaîne originale. J'aimerais vraiment un explication des raisons pour lesquelles cela ne fonctionne pas.

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

mise à Jour de 1

j'ai trouvé que si je fais ce script:

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

puis l'invoquer via:

ExecuteCommand("PowerShell.exe", "-File C:UsersPaulDesktopFoo.ps1",
  Environment.CurrentDirectory, DumpBytes, DumpBytes);

la première ligne est corrompue, mais la seconde ne l'est pas:

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

ceci me suggère que mon code de redirection fonctionne très bien; quand j'utilise Console.WriteLine en PowerShell J'obtiens UTF-8 comme je m'y attendais.

Cela signifie que Powershell's Write-Output et Write-Host les commandes doivent faire quelque chose de différent avec la sortie, et pas simplement appeler Console.WriteLine.

mise à Jour 2

j'ai même essayé ce qui suit pour forcer la page de code de la console PowerShell à UTF-8, mais Write-Host et Write-Output continuer à produire des résultats brisés tout en [Console]::WriteLine fonctionne.

$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
40
demandé sur jww 2014-03-12 14:49:56

4 réponses

ceci est un bug .NET. Lorsque PowerShell se lance, il cache la poignée de sortie (Console.Hors.) La propriété Encoding de cet auteur de texte ne prend pas la propriété Value StandardOutputEncoding.

lorsque vous le Modifiez depuis PowerShell, la propriété Encoding du rédacteur de sortie en cache renvoie la valeur en cache, de sorte que la sortie est toujours encodée avec l'encodage par défaut.

comme solution de contournement, je suggérerais de ne pas changer l'encodage. Il sera retourné à vous en tant que chaîne Unicode, à ce moment vous pouvez gérer l'encodage vous-même.