【问题标题】:UTF-8 output from PowerShell来自 PowerShell 的 UTF-8 输出
【发布时间】:2014-04-16 10:43:37
【问题描述】:

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

我尝试过的:

  • 通过-Command参数传递命令运行
  • 使用 UTF-8 编码将 PowerShell 脚本作为文件写入磁盘
  • 使用 UTF-8 和 BOM 编码将 PowerShell 脚本作为文件写入磁盘
  • 使用 UTF-16 将 PowerShell 脚本作为文件写入磁盘
  • 在我的控制台应用程序和 PowerShell 脚本中设置 Console.OutputEncoding
  • 在 PowerShell 中设置 $OutputEncoding
  • 设置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:\\Users\\Paul\\Desktop\\Foo.ps1",
  Environment.CurrentDirectory, DumpBytes, DumpBytes);

第一行已损坏,但第二行没有:

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

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

这意味着 PowerShell 的 Write-OutputWrite-Host 命令必须对输出执行不同的操作,而不是简单地调用 Console.WriteLine

更新 2

我什至尝试了以下方法来强制 PowerShell 控制台代码页为 UTF-8,但 Write-HostWrite-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

【问题讨论】:

  • 为什么从Powershell.exe开始而不是使用System.Management.Automationembed PowerShell right in the app
  • 我有一个朋友,他在System.Management.Automation 之上构建了一个完整的应用程序。但是在用户第 765 次投诉“我有一个脚本在 PowerShell.exe 下像这样工作,但在你的主机下像这样工作”之后,我,我的意思是我的朋友,决定放弃这个想法。
  • 我在这里发布了一些信息:octopusdeploy.com/blog/improving-powershell
  • 我认为你想多了......你只需要接受它是 UTF-16 的事实 ;-)
  • 为什么说是UTF-16?这似乎是 OEM 代码页

标签: powershell encoding utf-8 character-encoding io-redirection


【解决方案1】:

不是编码方面的专家,但是在阅读了这些之后......

... $OutputEncoding 变量仅影响通过管道传输到本机应用程序的数据似乎相当清楚。

如果从 withing PowerShell 发送到文件,编码可以由 out-file cmdlet 上的 -encoding 参数控制,例如

写输出“你好” |输出文件“enctest.txt”-编码 utf8

那么您在 PowerShell 前端无能为力,但以下帖子可能会对您有所帮助:。

【讨论】:

    【解决方案2】:

    这是 .NET 中的一个错误。当 PowerShell 启动时,它会缓存输出句柄 (Console.Out)。该文本编写器的 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
    

    【讨论】:

    • 感谢李的信息。 “它将作为Unicode字符串返回给您,此时您可以自己管理编码。”是什么意思。我正在尝试调用Encoding.GetEncoding(850).GetBytes(textOutputByPowershell),然后是Encoding.UTF8.GetString(),但这似乎也产生了错误的输出。
    • 它应该是一个 .net 字符串(因此,UTF16)。无论如何设置 StandardOutputEncoding 都不能保证工作,因为“设置此属性并不能保证进程将使用指定的编码......”话虽如此,我认为它默认为您的 Windows CodePage :-/
    • 谢谢大家,搞定了! (解决方案添加到原帖)
    • 你的解决方案是什么@PaulStovell?我在上面看不太清楚。处理写入 powershell 的标准输出的 python 代码,当源代码有 unicode 时,通过 cp1252 并不能很好地工作。
    【解决方案3】:

    [Console]::OuputEncoding 设置为您想要的任何编码,并使用[Console]::WriteLine 打印出来。

    如果powershell的输出方法有问题,那就不要用了。感觉有点糟糕,但就像一个魅力:)

    【讨论】:

      【解决方案4】:

      花了一些时间来解决我的问题,并认为它可能会引起人们的兴趣。我在尝试在 Windows 8 上使用 PowerShell 3.0 自动生成代码时遇到了问题。目标 IDE 是使用 MDK-ARM Essential Toolchain 5.24.1 的 Keil 编译器。与 OP 有点不同,因为我在预构建步骤中本机使用 PowerShell。当我尝试#include 生成的文件时,我收到了错误

      致命错误:检测到 UTF-16 (LE) 字节顺序标记“..\GITVersion.h” 但不支持编码

      我通过更改生成输出文件的行来解决问题:

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

      到:

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

      【讨论】:

        猜你喜欢
        • 2021-06-29
        • 2017-01-14
        • 2017-02-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-11
        • 2015-06-04
        相关资源
        最近更新 更多