使用Write-Output输出时不生成BOM。

7
如果我执行如下命令:
Write-Output March > a.txt

我得到了这个结果:

        U+FEFF    
M       U+004D          
a       U+0061          
r       U+0072    
c       U+0063          
h       U+0068 
        U+000D       
\n      U+000A       

我不想要BOM。我尝试了不同的方法,例如:

$OutputEncoding = [System.Text.UTF8Encoding]::new($false)
$PSDefaultParameterValues['*:Encoding'] = 'utf8'
[Console]::InputEncoding = [System.Text.UTF8Encoding]::new($false)
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)

但是它们都没有解决这个问题。注意,我正在使用PowerShell 5.1。我看到了一些类似的问题,但不完全与此问题相同,因为它们处理管道和外部命令。

2个回答

12

简而言之

  • 如果你想要Windows PowerShell>运算符和像Out-File这样的cmdlets以无BOM UTF-8输出,你唯一的选择是在整个系统中更改为该编码(下一节中请注意警告):

    • 作为一次性步骤,运行intl.cpl打开控制面板的区域设置,切换到管理选项卡,点击更改系统区域设置...按钮并勾选测试版:使用 Unicode UTF-8 支持全球各种语言。需要重新启动。

    • 另外,每次会话都要运行以下命令,最好通过你的$PROFILE文件来实现:

      • $PSDefaultParameterValues['*:Encoding'] = 'Default'
  • 否则,你必须直接使用.NET APIs - 参见这个问题的答案 - 或者编写一个适用于PowerShell的包装器 - 参见这个答案,它还展示了一个New-Item的替代方法。

  • 或者,你可以安装跨平台的PowerShell (Core) v6+版本,它始终默认为无BOM的UTF-8


从Windows 10开始,您可以使Windows PowerShell始终默认为无BOM的UTF-8编码 - 假设您愿意在整个系统中更改此编码:

  • 将系统区域设置(非 Unicode 程序的语言)更改为无 BOM 的 UTF-8,如 this answer 中所述:

    • 简而言之:运行 intl.cpl 打开控制面板的区域设置,切换到 管理 选项卡,点击 更改系统区域设置... 按钮,并勾选 测试版:使用 Unicode UTF-8 支持全球语言;请注意,您需要具有 管理员权限 才能进行此更改,并且需要 重新启动计算机 才能使更改生效。

    • 注意事项

      • 此更改将 OEM 和 ANSI 代码页都设置为 65001,即(无 BOM 的)UTF-8,这影响不仅所有控制台窗口,还包括所有旧版(非 Unicode)应用程序,包括 GUI 应用程序

      • 截至 Windows 11 版本 22H2,此功能仍处于测试版,可能会破坏旧版控制台应用程序。

  • 然后,在 Windows PowerShell v5.1 中,将以下内容添加到您的 $PROFILE 文件中(在 PowerShell (Core) v6+ 中不需要):

    • $PSDefaultParameterValues['*:Encoding'] = 'Default'
    • $OutputEncoding = [System.Text.Utf8Encoding]::new($false)

有了这个效果:

  • 所有具有-Encoding参数的文件写入Windows PowerShell cmdlet将默认为无BOM的UTF-8(Default表示活动ANSI代码页,然后将为65001,即无BOM的UTF-8),特别是包括> / Out-File / Set-Content

  • Windows PowerShell还会将无BOM文件作为UTF-8读取,包括源代码和通过Get-Content;通常,Windows PowerShell根据系统区域设置适当的ANSI代码页解释无BOM文件(而PowerShell(Core)v6+则假定UTF-8)。

  • 由于OEM代码页是无BOM的UTF-8(如chcp.com报告65001所反映的那样),因此PowerShell也将使用无BOM的UTF-8:

    • 在通过其CLI接收的数据的解释时。
    • 在PowerShell会话内接收来自外部程序的数据的解释时。
    • 上面的$OutputEncoding分配还确保PowerShell将数据作为无BOM的UTF-8发送到外部程序。 (这个首选项变量现在在PowerShell [Core] v6+中幸运地默认为无BOM的UTF-8。)
请注意,上述操作还会使所有的 PowerShell [Core] v6+ 控制台窗口在所有方面都使用无 BOM 的 UTF-8 编码,除了你不需要添加 $PROFILE(尽管它们没有任何坏处)。

背景信息:

  • > a.txt 实际上与 | Out-File a.txt 相同。

  • Windows PowerShell 的 > / >> / Out-File 默认使用 UTF-16LE(“Unicode”)[2],这必然会使用 BOM。

  • 您有两个选择不同编码的选项:

    • 显式地使用 Out-File 并使用其 -Encoding 参数。

    • 在 v5.1(以及 PowerShell [Core] v6+ 中),您可以通过 $PSDefaultParameterValues 首选项变量设置 > / >> / Out-File 的默认编码,如this answer中所讨论的那样。

    • 但是,在 Windows PowerShell 中,-Encodingutf8 值始终是带有 BOM 的 UTF-8 编码,因此 - 除非您愿意像上面解释的那样全局切换到 UTF-8 - 创建无 BOM 的 UTF-8 文件的唯一方法是直接使用 .NET API

      • 请注意,在 PowerShell [Core] v6+ 中,-Encoding 参数接受的 utf8 值现在(更明智地)指的是无 BOM 的 UTF-8 编码;如果您确实希望在那里使用 UTF-8 BOM,请改用 utf8BOM

关于您尝试的内容:
您尝试的属性和变量仅与PowerShell(在两个版本中)如何与外部程序通信有关:
  • $OutputEncoding 确定 PowerShell 在通过管道将数据发送到外部程序(后者可以通过标准输入 stdin 读取)时使用的编码。

  • [Console]::OutputEncoding 确定 PowerShell 在解释从外部程序接收到的输出时使用的编码。

  • [Console]::InputEncoding 是 PowerShell 在从外部接收数据时使用的编码,当其 CLI 被调用时。

    • 注意:在这种情况下,您无法在 PowerShell 会话中更改此编码,因为那将太晚了。
    • 它必须由调用者在调用 PowerShell CLI 之前设置,最容易的方法是从 cmd.exe 中使用 chcp 65001(请参见有关从 PowerShell 内部调用 chcp 的注意事项)。虽然这通常会同时设置 [Console]::InputEncoding[Console]::OutputEncoding,但这通常是可取的。

注意:

  • 在Windows上,默认情况下,[Console]::OutputEncoding[Console]::InputEncoding反映的是遗留系统区域设置的OEM代码页的编码,由chcp.com报告;在类Unix平台(PowerShell [Core] v6+),它(现今几乎例外)是(无BOM)UTF-8。

  • 由于这些.NET属性中的编码被缓存,您不能使用chcp.com从PowerShell内部更改这些属性 - 相反,请直接分配所需的编码。

  • 有关更多信息,请参见this answer,其中讨论了如何使Windows上的控制台窗口与外部程序一致地使用无BOM的UTF-8。


[1] 从技术上讲,这个偏好设置也适用于文件读取 cmdlet,这对于没有 BOM 的文件来说并不是严格必要的,对于带有 BOM 的文件也不会造成任何伤害 - 即使该 BOM 指示了 UTF-16 或 UTF-32 编码 - 因为 BOM 总是优先于 -Encoding 参数。

[2] 不幸的是,在 Windows PowerShell 中,默认编码在各个 cmdlet 之间变化很大 - 请参见 this answer 的底部部分。


系统区域设置可以通过命令行界面或组策略进行设置吗? - gargoylebident
此外,向个人资料添加这两行似乎会触发第3行的添加“値䑓晥畡瑬慐慲敭整噲污敵孳⨢䔺据摯湩≧⁝‽䐢晥畡瑬ഢ␊畏灴瑵湅潣楤杮㴠嬠祓瑳浥吮硥⹴呕㡆湅潣楤杮㩝渺睥⤨਍”(我不知道它的含义),这导致每次打开PowerShell时显示错误:https://imgur.com/a/c5Qfoz3 - gargoylebident
1
@gargoylebident,关于CLI:我认为是的,通过注册表;关于GPO:不确定-但我鼓励您专门为此创建一个新问题。至于配置文件:听起来像是编码不匹配,如果您将UTF-8或ANSI编码字符添加到UTF-16LE(“Unicode”)文件中,则会出现这种情况。 - mklement0
1
谢谢,你总是正确的。我在应用系统区域设置后使用 | Out-File $PROFILE.AllUsersCurrentHost -Append 添加了两个字符串,而且配置文件(5.1版本)是UTF-16LE编码(与Powershell现在默认的UTF-8不同)。在更改区域设置之前添加(或在添加时使用16LE编码)可以解决这个问题。 - gargoylebident
1
很高兴听到这个消息,@gargoylebident。请注意,对于追加到现有文件中,“Add-Content”是最佳选择,因为它会尝试匹配现有的编码。(如果文件尚不存在,则其行为类似于“Set-Content”,这意味着在Windows PowerShell中使用ANSI,在PowerShell Core中使用无BOM的UTF-8)。 - mklement0

1
如果您只使用ASCII字符,则在PowerShell 5.1中使用set-content即可:
Write-Output March | set-content a.txt
'March' | set-content a.txt

或者在您的$profile中使用此哈希表将out-file的默认编码设置为ascii。 out-file的默认编码是utf16或“unicode”编码。'>'是out-file的快捷方式。键的名称必须加引号,因为它包含冒号。 utf8nobom直到较新的powershell版本才可用。 '>>'也调用out-file,并且可能在同一文件中混合编码。

$PSDefaultParameterValues = @{ 'out-file:encoding' = 'ascii' }

然后这将生成一个ASCII文件:
Write-Output March > a.txt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接