注意:
下一个部分主要适用于Windows PowerShell。
在这两种情况下,这些信息适用于使PowerShell使用UTF-8来读写文件。
现在可以进行全系统范围的UTF-8切换(自Windows 10的最新版本以来):请参阅this answer,但请注意以下注意事项:
- 该功能具有深远的影响,因为OEM和ANSI代码页都将设置为
65001
,即UTF-8;此外,该功能在撰写本文时(Windows 11 22H2)仍被视为测试版功能。
- 在Windows PowerShell中,这仅对默认为ANSI代码页的那些cmdlet生效,特别是
Set-Content
,但不适用于Out-File
/ >
,并且还适用于读取文件,特别是包括Get-Content
和PowerShell本身读取源代码。
Windows PowerShell 视角:
在PSv5.1或更高版本中,其中">"和">>"实际上是"Out-File"的别名,您可以通过"$PSDefaultParameterValues"首选项变量来设置">" / ">>" / "Out-File"的默认编码:
$PSDefaultParameterValues['Out-File:Encoding'] = 'utf8'
注意:
在Windows PowerShell(最新和最终版本为v5.1的传统版本)中,这总是创建带有伪BOM的UTF-8文件。
许多基于Unix的实用程序不识别此BOM(请参阅底部);请参阅此帖子以获取创建无BOM的UTF-8文件的解决方法。
在PowerShell(Core)v6+中,默认情况下是无BOM的UTF-8(请参阅下一节),但如果您确实希望在那里使用BOM,则可以使用'utf8BOM'
在PSv5.0或更低版本中,您无法更改">" / ">>"的编码,但是在PSv3或更高版本中,上述技术对于对"Out-File"的显式调用确实有效。
("$PSDefaultParameterValues"首选项变量是在PSv3.0中引入的)。
在PSv3.0或更高版本中,如果您想为支持"-Encoding"参数的所有cmdlet(在PSv5.1+中包括">"和">>")设置默认编码,请使用:
$PSDefaultParameterValues['*:Encoding'] = 'utf8'
如果您将此命令放在您的$PROFILE中,像Out-File和Set-Content这样的cmdlet将默认使用UTF-8编码。但请注意,这将成为一个会话全局设置,将影响所有未显式指定编码的命令/脚本,通过它们的-Encoding参数。
同样地,确保在您希望以相同方式运行的脚本或模块中包含这样的命令,以便它们在由其他用户或不同的机器运行时确实表现相同。然而,为了避免会话全局更改,使用以下形式创建$PSDefaultParameterValues的本地副本:
$PSDefaultParameterValues = @{ '*:Encoding' = 'utf8' }
对于Windows PowerShell标准cmdlet中许多不一致的默认字符编码行为的摘要,请参阅底部部分。
自动变量
$OutputEncoding
与此无关,仅适用于PowerShell与外部程序的通信方式(PowerShell向其发送字符串时使用的编码方式),与输出重定向运算符和PowerShell cmdlet用于保存到文件的编码方式无关。
可选阅读:跨平台视角:PowerShell Core:
通过其PowerShell Core版本,PowerShell现在已经实现了跨平台,其编码默认为无BOM的UTF-8,与类Unix平台保持一致。
这意味着没有BOM的源代码文件被认为是UTF-8编码的,并且使用">" / "Out-File" / "Set-Content"默认为无BOM的UTF-8;显式使用"utf8" "-Encoding"参数也会创建无BOM的UTF-8,但您可以选择使用"utf8bom"值创建带有伪BOM的文件。
如果您在类Unix平台上使用编辑器创建PowerShell脚本,甚至在Windows上使用跨平台编辑器如Visual Studio Code和Sublime Text,生成的*.ps1文件通常不会有UTF-8伪BOM:
这在PowerShell Core上运行良好。
如果文件包含非ASCII字符,则在Windows PowerShell上可能会出现问题;如果您确实需要在脚本中使用非ASCII字符,请将它们保存为带有BOM的UTF-8。
没有BOM,Windows PowerShell会将您的脚本误解为使用传统的"ANSI"代码页进行编码(由系统区域设置确定,用于非Unicode应用程序;例如,在美国英语系统上为Windows-1252)。
相反,具有UTF-8伪BOM的文件在类Unix平台上可能会引起问题,因为它们会导致Unix实用程序(如cat、sed和awk)以及一些编辑器(如gedit)将伪BOM传递,即将其视为数据。
这可能不总是问题,但确实可能是问题,例如当您尝试在bash中将文件读入字符串时,比如text=$(cat file)或text=$(
Windows PowerShell中不一致的默认编码行为:
遗憾的是,Windows PowerShell中使用的默认字符编码非常不一致;如前一节所讨论的跨平台PowerShell Core版本已经令人称赞地解决了这个问题。
注意:
写入的Cmdlets:
Out-File
和 >
/ >>
默认创建“Unicode” - UTF-16LE - 文件,其中每个ASCII范围的字符(也)由2个字节表示 - 这与Set-Content
/ Add-Content
(见下一点)明显不同;New-ModuleManifest
和 Export-CliXml
也创建UTF-16LE文件。
Set-Content
(如果文件尚不存在/为空,则Add-Content
)使用ANSI编码(由活动系统区域设置的ANSI遗留代码页指定的编码,PowerShell称之为Default
)。
Export-Csv
确实创建ASCII文件,如文档所述,但请参阅下面关于-Append
的注释。
Export-PSSession
默认创建带有BOM的UTF-8文件。
New-Item -Type File -Value
目前创建的是无BOM的UTF-8文件。
Send-MailMessage
帮助主题也声称ASCII编码是默认值 - 我个人尚未验证该声明。
Start-Transcript
无论如何都会创建带有BOM的UTF-8文件,但请参阅下面关于 -Append
的注释。
关于追加到现有文件的命令:
>>
/ Out-File -Append
不会尝试匹配文件的现有内容的编码。
也就是说,它们会盲目地应用默认编码,除非使用 -Encoding
进行其他指示,但 >>
不支持该选项(除非通过 $PSDefaultParameterValues
在PSv5.1+中间接设置,如上所示)。
简而言之:您必须知道现有文件内容的编码,并使用相同的编码进行追加。
Add-Content
是一个值得称赞的例外:在没有明确的-Encoding
参数的情况下,它会检测现有的编码并自动应用于新内容。谢谢,js2010。请注意,在Windows PowerShell中,如果现有内容没有BOM,则应用的是ANSI编码,而在PowerShell Core中则是UTF-8编码。
这种Out-File -Append
/ >>
和Add-Content
之间的不一致性,也影响到PowerShell Core,在GitHub问题#9423中有讨论。
Export-Csv -Append
部分匹配现有的编码:如果现有文件的编码是ASCII/UTF-8/ANSI之一,它会盲目地追加UTF-8,但正确匹配UTF-16LE和UTF-16BE。
换句话说:在没有BOM的情况下,Export-Csv -Append
假设为UTF-8,而Add-Content
假设为ANSI。
Start-Transcript -Append
部分匹配现有的编码:它正确匹配带有BOM的编码,但在没有BOM的情况下默认使用可能有损失的ASCII编码。
Cmdlets that read (that is, the encoding used in the absence of a BOM):
Get-Content and Import-PowerShellDataFile default to ANSI (Default), which is consistent with Set-Content.
ANSI is also what the PowerShell engine itself defaults to when it reads source code from files.
By contrast, Import-Csv, Import-CliXml and Select-String assume UTF-8 in the absence of a BOM, and so does the switch statement with its -File parameter.