如何将没有 BOM 的 UTF8 写入控制台(无文件)?

How do I write UTF8 with no BOM to console (no file)?

我有一个 powershell 脚本,可以通过 Write-Output returns 一些字符串。 我希望这些行是没有 bom 的 UTF8。我不想要一个全局设置,我只希望它对我当时写的那几行有效。

另一个问题帮助我明白了一点:Using PowerShell to write a file in UTF-8 without the BOM

我从其中一个答案中得到灵感,写了如下代码:

$mystr = "test 1 2 3"
$mybytes = [Text.Encoding]::UTF8.GetBytes($mystr)
$OutStream = [console]::OpenStandardOutput()
$OutStream.Write($mybytes,0,$TestBytes.Length)
$OutStream.Close()

然而,此代码仅写入标准输出,如果我尝试重定向它,它会忽略我的请求。换句话说,将该代码放在 test.ps1 和 运行 test.ps1 >out.txt 中仍然会打印到控制台而不是 out.txt.

有人可以推荐我如何编写这段代码,以防万一用户通过 > 将我的 PS 的输出重定向到一个文件,该输出是没有 BOM 的 UTF8 格式吗?

编码用于将文本保存到文件,而不是用于写入控制台。您的重定向运算符 > 是保存内容的运算符,这意味着它决定编码。 Powershell 中的重定向使用 Unicode。如果需要使用其他编码,则不能使用重定向。

When you are writing to files, the redirection operators use Unicode encoding. If the file has a different encoding, the output might not be formatted correctly. To redirect content to non-Unicode files, use the Out-File cmdlet with its Encoding parameter.

来源:about_redirection

通常你会用ex。 Out-File -Path test.txt -Encoding UTF8 在您的脚本中,但它包含 BOM,因此我建议使用 WriteAllLines(path,contents),它默认使用不带 BOM 的 UTF8。

[System.IO.File]::WriteAllLines("c:\test.txt", $MyOutputArray)

添加到

  • 您最终想要实现的是将原始字节流写入 PowerShell 的 success-output 流(相当于 stdout 传统 shell[0] ), 而不是控制台.

    • 成功输出流是 PowerShell 中用于相互传递数据的命令,包括 output-redirection 运算符 >,此时不涉及控制台.

    • (写入 success-output 流的数据可能 最终 显示在控制台中,即如果流既没有在变量中捕获也没有重定向到其他地方。)

  • 但是,不可能发送原始字节流到PowerShell的成功输出流;只能发送 objects(.NET 类型的实例),因为 PowerShell 基本上是 object-oriented.

    • 即使数据表示字节流也必须作为.NET对象发送,例如[byte[]]数组。

      • 但是,将 [byte[]] 数组直接重定向到具有 > 的文件,不会 写入数组的原始字节,因为 >创建一个 "Unicode"(UTF-16LE 编码[1]text 数组的表示(如您将数组打印到控制台时所见)。
    • 为了将对象编码为字节流(通常编码为 text)以用于 external sinks 例如文件,您需要 PowerShell cmdlet(例如,Set-Content)、>(输出重定向运算符)或适当的 .NET 类型的方法(例如,[System.IO.File])的帮助,除了 2 种特殊情况:

      • 管道外部程序时,隐式使用存储在首选项变量$OutputEncoding中的编码。
      • 当打印到控制台时,存储在[Console]::OutputEncoding中的编码被隐式使用;此外,来自外部程序的输出 被假定为以这种方式编码[2] .
    • 一般在text输出时,使用[=16=等输出cmdlet的-Encoding参数比较简单] 让该 cmdlet 执行编码,而不是尝试在单独的第一步中获取字节表示。

      • 但是,BOM-less UTF-8 编码无法在 Windows PowerShell 中以这种方式选择(它 can 在 PowerShell Core) 中,因此使用显式字节表示 一个选项,结合 Set-Content -Encoding Byte [3] ;例如:

        # Write string "hü" to a UTF-8-encoded file *without BOM*:
        [Text.Encoding]::UTF8.GetBytes('hü') | 
          Set-Content -Encoding Byte file.txt
        

[0] 从 PowerShell 中写入 stdout,正如您尝试的那样,绕过 PowerShell's own system of output streams and prints directly to the console. (As an aside: Console.OpenStandardOutput()旨在绕过重定向,即使在传统 shell 的上下文中也是如此。)

[1] 在 PowerShell v5.0 之前,您无法更改 > 使用的编码;在 PSv5.1 及更高版本中,您可以使用 $PSDefaultParameterValues['Out-File:Encoding']='UTF8' 之类的东西 - 但是仍然会包含 BOM。有关背景,请参阅我的

[2] 有一个值得注意的不对称性:在将文本 发送到 外部程序时,$OutputEncoding 默认为 ASCII(仅限 7 位)编码,这意味着任何 non-ASCII 字符都会音译为 文字 ? 字符。;相比之下,在从外部程序解释文本时,适用的[Console]::OutputEncoding默认为系统的活动遗留OEM代码页,这是一个8 位编码。参见 list of code pages supported by Windows.

[3] 当然,传递字节并不是真正的编码;可能出于这个原因 -Encoding Byte 已从 PowerShell Core 中删除,其中必须使用 -AsByteStream