Sortie UTF-8 de PowerShell

J’essaie d’utiliser Process.Start avec des E / S redirigées pour appeler PowerShell.exe avec une chaîne et récupérer la sortie, le tout en UTF-8 . Mais je ne semble pas être capable de faire ce travail.

Ce que j’ai essayé:

  • Passer la commande à exécuter via le paramètre -Command
  • Ecriture du script PowerShell en tant que fichier sur disque avec codage UTF-8
  • Ecriture du script PowerShell en tant que fichier sur disque avec UTF-8 avec codage BOM
  • Ecriture du script PowerShell en tant que fichier sur disque avec UTF-16
  • Définition de Console.OutputEncoding dans mon application console et dans le script PowerShell
  • Définition de $OutputEncoding dans PowerShell
  • Définition de Process.StartInfo.StandardOutputEncoding
  • Tout faire avec Encoding.Unicode au lieu de Encoding.UTF8

Dans tous les cas, lorsque j’examine les octets que je reçois, j’obtiens des valeurs différentes à ma chaîne d’origine. J’aimerais vraiment avoir une explication de la raison pour laquelle cela ne fonctionne pas.

Voici mon code:

 static void Main(ssortingng[] 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(ssortingng text) { Console.Write(text + " " + ssortingng.Join(",", Encoding.UTF8.GetBytes(text).Select(b => b.ToSsortingng("X")))); Console.WriteLine(); } static int ExecuteCommand(ssortingng executable, ssortingng arguments, ssortingng workingDirectory, Action output, Action 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(ssortingng.Format("Error when attempting to execute {0}: {1}", executable, ex.Message), ex); } } 

Mise à jour 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 invoquez-le via:

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

La première ligne est corrompue, mais la seconde n’est pas:

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

Cela me suggère que mon code de redirection fonctionne correctement; Quand j’utilise Console.WriteLine dans PowerShell, je reçois UTF-8 comme je l’espérais.

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

Mise à jour 2

J’ai même essayé de forcer la page de code de la console PowerShell sur UTF-8, mais Write-Host et Write-Output continuent à produire des résultats brisés lorsque [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 

Ceci est un bogue dans .NET. Lorsque PowerShell est lancé, il met en cache le handle de sortie (Console.Out). La propriété Encoding de cet éditeur de texte ne prend pas en compte la propriété StandardOutputEncoding.

Lorsque vous le modifiez à partir de PowerShell, la propriété Encoding de l’éditeur de sortie en cache renvoie la valeur mise en cache, de sorte que la sortie est toujours codée avec le codage par défaut.

En guise de solution de contournement, je suggère de ne pas modifier l’encodage. Il vous sera renvoyé sous la forme d’une chaîne Unicode, à quel point vous pouvez gérer vous-même l’encodage.

Exemple de mise en cache:

 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 

Pas un expert en encodage, mais après avoir lu ces …

… il semble assez clair que la variable $ OutputEncoding affecte uniquement les données acheminées vers les applications natives.

Si vous envoyez un fichier depuis PowerShell, le codage peut être contrôlé par le paramètre -encoding de la cmdlet out-file par exemple

 écriture-sortie "bonjour" |  out-file "enctest.txt" -encoding utf8

Vous ne pouvez rien faire d’autre sur le front PowerShell, mais le message suivant peut vous aider:

Définissez la [Console]::OuputEncoding comme codant ce que vous voulez et imprimez avec [Console]::WriteLine .

Si la méthode de sortie de PowerShell présente un problème, ne l’utilisez pas. C’est un peu mauvais, mais ça marche comme un charme 🙂

J’ai passé du temps à trouver une solution à mon problème et j’ai pensé que cela pouvait être intéressant. J’ai rencontré un problème en essayant d’automatiser la génération de code à l’aide de PowerShell 3.0 sur Windows 8. L’IDE cible était le compilateur Keil utilisant MDK-ARM Essential Toolchain 5.24.1. Un peu différent de l’OP, car j’utilise PowerShell en mode natif pendant l’étape de pré-construction. Lorsque j’ai essayé d’inclure le fichier généré, j’ai reçu l’erreur

Erreur fatale: Ordre des octets UTF-16 (LE) détecté ‘.. \ GITVersion.h’ mais l’encodage n’est pas pris en charge

J’ai résolu le problème en modifiant la ligne qui générait le fichier de sortie à partir de:

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

à:

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