来自 PowerShell 的 UTF-8 输出 [英] UTF-8 output from PowerShell

查看:52
本文介绍了来自 PowerShell 的 UTF-8 输出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用带有重定向 I/O 的 Process.Start 来调用带有字符串的 PowerShell.exe,并返回输出,所有这些都在 UTF-8.但我似乎无法完成这项工作.

I'm trying to use Process.Start with redirected I/O to call PowerShell.exe with a string, and to get the output back, all in UTF-8. But I don't seem to be able to make this work.

我尝试过的:

  • 通过-Command参数传递运行命令
  • 使用 UTF-8 编码将 PowerShell 脚本作为文件写入磁盘
  • 使用 UTF-8 和 BOM 编码将 PowerShell 脚本作为文件写入磁盘
  • 使用 UTF-16 将 PowerShell 脚本作为文件写入磁盘
  • 在我的控制台应用程序和 PowerShell 脚本中设置 Console.OutputEncoding
  • 在 PowerShell 中设置 $OutputEncoding
  • 设置Process.StartInfo.StandardOutputEncoding
  • 使用 Encoding.Unicode 而不是 Encoding.UTF8
  • Passing the command to run via the -Command parameter
  • Writing the PowerShell script as a file to disk with UTF-8 encoding
  • Writing the PowerShell script as a file to disk with UTF-8 with BOM encoding
  • Writing the PowerShell script as a file to disk with UTF-16
  • Setting Console.OutputEncoding in both my console application and in the PowerShell script
  • Setting $OutputEncoding in PowerShell
  • Setting Process.StartInfo.StandardOutputEncoding
  • Doing it all with Encoding.Unicode instead of Encoding.UTF8

在每种情况下,当我检查给定的字节时,我会得到与原始字符串不同的值.我真的很想解释为什么这不起作用.

In every case, when I inspect the bytes I'm given, I get different values to my original string. I'd really love an explanation as to why this doesn't work.

这是我的代码:

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

我发现如果我制作这个脚本:

Update 1

I found that if I make this script:

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

然后通过以下方式调用它:

Then invoke it via:

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

第一行已损坏,但第二行未损坏:

The first line is corrupted, but the second isn't:

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

这表明我的重定向代码一切正常;当我在 PowerShell 中使用 Console.WriteLine 时,我得到了预期的 UTF-8.

This suggests to me that my redirection code is all working fine; when I use Console.WriteLine in PowerShell I get UTF-8 as I expect.

这意味着 PowerShell 的 Write-OutputWrite-Host 命令必须对输出做一些不同的事情,而不是简单地调用 Console.WriteLine.

This means that PowerShell's Write-Output and Write-Host commands must be doing something different with the output, and not simply calling Console.WriteLine.

我什至尝试了以下方法来强制 PowerShell 控制台代码页为 UTF-8,但是 Write-HostWrite-Output 继续产生损坏的结果,同时[Console]::WriteLine 有效.

I've even tried the following to force the PowerShell console code page to UTF-8, but Write-Host and Write-Output continue to produce broken results while [Console]::WriteLine works.

$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

推荐答案

这是 .NET 中的一个错误.当 PowerShell 启动时,它会缓存输出句柄 (Console.Out).该文本编写器的 Encoding 属性没有选择 StandardOutputEncoding 属性的值.

This is a bug in .NET. When PowerShell launches, it caches the output handle (Console.Out). The Encoding property of that text writer does not pick up the value StandardOutputEncoding property.

当您在 PowerShell 中更改它时,缓存输出编写器的 Encoding 属性返回缓存值,因此输出仍使用默认编码进行编码.

When you change it from within PowerShell, the Encoding property of the cached output writer returns the cached value, so the output is still encoded with the default encoding.

作为一种解决方法,我建议不要更改编码.它将作为Unicode字符串返回给您,此时您可以自己管理编码.

As a workaround, I would suggest not changing the encoding. It will be returned to you as a Unicode string, at which point you can manage the encoding yourself.

缓存示例:

102 [C:Usersleeholm]
>> $r1 = [Console]::Out

103 [C:Usersleeholm]
>> $r1

Encoding                                          FormatProvider
--------                                          --------------
System.Text.SBCSCodePageEncoding                  en-US



104 [C:Usersleeholm]
>> [Console]::OutputEncoding = [System.Text.Encoding]::UTF8

105 [C:Usersleeholm]
>> $r1

Encoding                                          FormatProvider
--------                                          --------------
System.Text.SBCSCodePageEncoding                  en-US

这篇关于来自 PowerShell 的 UTF-8 输出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆