【问题标题】:How do I write UTF8 with no BOM to console (no file)?如何将没有 BOM 的 UTF8 写入控制台(无文件)?
【发布时间】:2020-12-08 09:39:52
【问题描述】:

我有一个 powershell 脚本,它通过 Write-Output 返回一些字符串。 我希望这些行是没有 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?

【问题讨论】:

  • 你的问题标题没有意义。 UTF-8 和 BOM 与文件有关,与控制台无关。您的描述文字并没有使它更清晰。你想达到什么目标,它是如何失败的?你不能改变用户重定向他的东西的方式。告诉用户使用Out-File -Encoding ascii(如果您不需要任何特殊字符)。
  • 我不同意这是没有意义的。这是一个 PS 怪癖。换句话说,CMD,“echo hello world > test.txt”按预期工作,并准确写入我管道的字节。在 PS 中,如果我执行 Write-Output "hello world" > test.txt,它会假设我正在写什么并插入一个 BOM。我在问是否有办法重定向二进制输出(因此是字节数组)。但是我会关闭,因为显然这不是 PS 的工作方式。
  • 您确实要求“将没有 BOM 的 UTF8 写入控制台”。控制台没有 UTF8 或 BOM 的概念。写入文件时使用编码,而不是在将文本打印到控制台时使用。
  • 大多数(如果不是全部)shell 允许您对字符串进行预编码,并将原始输入(例如二进制)写出,可以重定向到文件。 Powershell 没有,这很好。这个问题是从-3 到+1,我投票关闭它。如果您认为这是一个不好的问题,也可以投票关闭它。我希望我可以删除它,但我不想删除乐于助人的人的答案。我不认为这是一个坏问题,当您尝试与您可能有经验的其他 shell 等效时。您对我的问题没有意义的解释对于那些刚接触 powershell 的人来说并不明显。
  • 现在我相信你不明白 UTF-8 和 BOM 的真正含义,因为你仍然没有意义。在 PowerShell 中,您还可以构造任何字节数组并将其写入文件(参见此处:cyber-defense.sans.org/blog/2010/02/11/…)。因此,如果您想这样做,您可以自己构建一个 BOM 并将所有内容编码为 UTF-8,但是您在控制台中也会遇到这个混乱......

标签: powershell powershell-3.0


【解决方案1】:

添加到Frode F.'s helpful answer

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

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

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

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

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

      • 但是,将[byte[]] 数组直接重定向到带有> 的文件,不会 写入数组的原始字节,因为> 创建了一个“Unicode”(UTF-16LE 编码[1]) text 数组的表示形式(就像您将数组打印到控制台时所看到的那样)。
    • 为了将对象编码为外部接收器(例如文件)的字节流(通常编码为文本),您需要PowerShell cmdlet 的帮助(例如, Set-Content)、>(输出重定向运算符)或适当的 .NET 类型的方法(例如,[System.IO.File]),但在 2 种特殊情况下除外:

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

      • 但是,在 Windows PowerShell 中不能以这种方式选择 BOM-less UTF-8 编码(它可以在 PowerShell Core 中),所以使用显式字节表示 is 是一个选项,结合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 并直接打印到 控制台。 (顺便说一句:Console.OpenStandardOutput() 旨在绕过重定向,即使在传统 shell 的上下文中也是如此。)

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

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

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

【讨论】:

  • 谢谢。我认为标准输出(成功输出)和控制台之间的区别最终解释了为什么我观察到奇怪的行为(对我来说)。我重做了我的模块以解决我的困惑。很好的答案。
【解决方案2】:

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

当你 写入文件时,重定向运算符使用 Unicode 编码。如果 该文件具有不同的编码,输出可能未格式化 正确。要将内容重定向到非 Unicode 文件,请使用 Out-File cmdlet 及其 Encoding 参数。

来源:about_redirection

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

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

【讨论】:

  • 顺便说一句:幸运的是,在 v5.1+ 中,您现在可以控制 > / >> 使用的编码,尽管这并不明显:@987654330 @ - 但是,它仍将包含 BOM。请参阅stackoverflow.com/a/42451413/45375 了解更多信息。
猜你喜欢
  • 2020-09-01
  • 2015-12-12
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
  • 1970-01-01
  • 2017-09-30
  • 2014-02-14
相关资源
最近更新 更多