了解 VS Code 和 PowerShell 中的文件编码

使用 VS Code 创建和编辑 PowerShell 脚本时,请务必使用正确的字符编码格式保存文件。

什么是文件编码,为什么很重要?

VS Code 管理人类在缓冲区中输入字符字符串以及读取/写入文件系统的字节块之间的接口。 当 VS Code 保存文件时,它使用文本编码来确定每个字符的字节数。 有关详细信息,请参阅 about_Character_Encoding

同样,当 PowerShell 运行脚本时,它必须将文件中的字节转换为字符,以将文件重新构造为 PowerShell 程序。 由于 VS Code 写入文件,并且 PowerShell 读取该文件,因此需要使用相同的编码系统。 分析 PowerShell 脚本的过程包括:字节令牌抽象语法树执行

VS Code 和 PowerShell 都安装了合理的默认编码配置。 但是,PowerShell 使用的默认编码随 PowerShell 6 的发布而更改。 若要确保在 VS Code 中使用 PowerShell 或 PowerShell 扩展时没有问题,需要正确配置 VS Code 和 PowerShell 设置。

编码问题的常见原因

当 VS Code 或脚本文件的编码与 PowerShell 的预期编码不匹配时,会发生编码问题。 PowerShell 无法自动确定文件编码。

使用 7 位 ASCII 字符集中的字符时,更有可能遇到编码问题。 例如:

  • 扩展的非字母字符,如 em-dash()、非中断空格( )或左双引号("
  • 着色拉丁字符(Éü
  • 西里尔文等非拉丁文字符(ДЦ
  • 中日韩字符(

编码问题的常见原因是:

  • VS Code 和 PowerShell 的编码尚未从默认值更改。 对于 PowerShell 5.1 及更低版本,默认编码不同于 VS Code。
  • 另一个编辑器已打开并覆盖新编码中的文件。 这通常发生在 ISE 中。
  • 该文件在与 VS Code 或 PowerShell 预期不同的编码中签入源代码管理。 当协作者使用不同的编码配置使用编辑器时,可能会发生这种情况。

如何判断何时遇到编码问题

通常,编码错误在脚本中呈现为分析错误。 如果在脚本中发现奇怪的字符序列,可能是问题。 在下面的示例中,en-dash()显示为字符 â€"

Send-MailMessage : A positional parameter cannot be found that accepts argument 'Testing FuseMail SMTP...'.
At C:\Users\<User>\<OneDrive>\Development\PowerShell\Scripts\Send-EmailUsingSmtpRelay.ps1:6 char:1
+ Send-MailMessage â&euro;"From $from â&euro;"To $recipient1 â&euro;"Subject $subject  ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Send-MailMessage], ParameterBindingException
    + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.SendMailMessage

出现此问题的原因是 VS Code 将 UTF-8 中的字符 编码为字节 0xE2 0x80 0x93。 将这些字节解码为 Windows-1252 时,它们将被解释为字符 â&euro;"

你可能会看到的一些奇怪的字符序列包括:

  • â&euro;" 而不是 (短划线)
  • â&euro;" 而不是 (em-dash)
  • Ä2 而不是 Ä
  • Â 而不是  (非中断性空间)
  • Ã&copy; 而不是 é

此方便的 参考 列出了指示 UTF-8/Windows-1252 编码问题的常见模式。

VS Code 中的 PowerShell 扩展如何与编码交互

PowerShell 扩展通过多种方式与脚本交互:

  1. 在 VS Code 中编辑脚本时,VS Code 会将内容发送到扩展。 语言服务器协议 强制在 UTF-8 中传输此内容。 因此,扩展无法获取错误的编码。
  2. 直接在集成控制台中执行脚本时,PowerShell 会直接从文件中读取这些脚本。 如果 PowerShell 的编码不同于 VS Code,则此处可能会出错。
  3. 当 VS Code 中打开的脚本引用 VS Code 中未打开的另一个脚本时,该扩展会回退到从文件系统加载该脚本的内容。 PowerShell 扩展默认为 UTF-8 编码,但使用 字节顺序标记或 BOM 检测来选择正确的编码。

假设无 BOM 格式的编码(如不带 BOM 的 UTF-8Windows-1252),则会出现此问题。 PowerShell 扩展默认为 UTF-8。 该扩展无法更改 VS Code 的编码设置。 有关详细信息,请参阅 问题 #824

选择正确的编码

不同的系统和应用程序可以使用不同的编码:

  • 在 .NET Standard、Web 和 Linux 世界中,UTF-8 现在是主导编码。
  • 许多 .NET Framework 应用程序使用 UTF-16。 出于历史原因,这有时称为“Unicode”,该术语现在指的是包括 UTF-8 和 UTF-16 的广泛 标准
  • 在 Windows 上,许多早于 Unicode 的本机应用程序默认继续使用 Windows-1252。

Unicode 编码也有字节顺序标记(BOM)的概念。 BOM 出现在文本的开头,告知解码器使用哪个编码文本。 对于多字节编码,BOM 还指示编码 字节。 BOM 设计为非 Unicode 文本中很少发生的字节,允许合理猜测 BOM 时文本为 Unicode。

BOM 是可选的,其采用在 Linux 世界中并不那么受欢迎,因为随处都使用了可靠的 UTF-8 约定。 大多数 Linux 应用程序假定文本输入在 UTF-8 中编码。 虽然许多 Linux 应用程序会识别并正确处理 BOM,但数字不会,导致使用这些应用程序操作的文本中的项目。

因此,

  • 如果主要使用 Windows 应用程序和 Windows PowerShell,则应首选使用 BOM 或 UTF-16 的 UTF-8 等编码。
  • 如果跨平台工作,则应更喜欢使用 BOM 的 UTF-8。
  • 如果主要在 Linux 关联的上下文中工作,则应首选 UTF-8 而不使用 BOM。
  • Windows-1252 和 latin-1 本质上是应避免的旧编码(如果可能)。 但是,某些较旧的 Windows 应用程序可能依赖于它们。
  • 值得注意的是,脚本签名 依赖编码的,这意味着对已签名脚本的编码更改将需要辞职。

配置 VS Code

VS Code 的默认编码为不带 BOM 的 UTF-8。

若要设置 VS Code 的编码,请转到 VS Code 设置(Ctrl+),并设置 "files.encoding" 设置:

"files.encoding": "utf8bom"

一些可能的值为:

  • utf8: [UTF-8] 没有 BOM
  • utf8bom:带 BOM 的 [UTF-8]
  • utf16le: 小端 [UTF-16]
  • utf16be:Big endian [UTF-16]
  • windows1252:[Windows-1252]

应在 GUI 视图中获取此内容的下拉列表,或在 JSON 视图中获取该下拉列表。

还可以在可能的情况下添加以下内容以自动检测编码:

"files.autoGuessEncoding": true

如果不希望这些设置影响所有文件类型,VS Code 还允许每种语言配置。 通过将设置放在 [<language-name>] 字段中来创建特定于语言的设置。 例如:

"[powershell]": {
    "files.encoding": "utf8bom",
    "files.autoGuessEncoding": true
}

还可以考虑安装适用于 Visual Studio Code 的 Gremlins 跟踪器。 此扩展显示某些容易损坏的 Unicode 字符,因为它们不可见或看起来像其他普通字符。

配置 PowerShell

PowerShell 的默认编码因版本而异:

  • 在 PowerShell 6+ 中,默认编码为所有平台上没有 BOM 的 UTF-8。
  • 在 Windows PowerShell 中,默认编码通常是 Windows-1252,这是 latin-1(也称为 ISO 8859-1)的扩展。

在 PowerShell 5+ 中,可以使用以下代码找到默认编码:

[psobject].Assembly.GetTypes() | Where-Object { $_.Name -eq 'ClrFacade'} |
  ForEach-Object {
    $_.GetMethod('GetDefaultEncoding', [System.Reflection.BindingFlags]'nonpublic,static').Invoke($null, @())
  }

以下 脚本 可用于确定没有 BOM 的脚本推断的 PowerShell 会话编码。

$badBytes = [byte[]]@(0xC3, 0x80)
$utf8Str = [System.Text.Encoding]::UTF8.GetString($badBytes)
$bytes = [System.Text.Encoding]::ASCII.GetBytes('Write-Output "') + [byte[]]@(0xC3, 0x80) + [byte[]]@(0x22)
$path = Join-Path ([System.IO.Path]::GetTempPath()) 'encodingtest.ps1'

try
{
    [System.IO.File]::WriteAllBytes($path, $bytes)

    switch (& $path)
    {
        $utf8Str
        {
            return 'UTF-8'
            break
        }

        default
        {
            return 'Windows-1252'
            break
        }
    }
}
finally
{
    Remove-Item $path
}

可以将 PowerShell 配置为更普遍地使用配置文件设置使用给定编码。 请参阅以下文章:

  • 有关 Stack Overflow上的 PowerShell 编码的 答案。
  • 关于在 PowerShell中处理无 BOM UTF-8 输入的 博客文章。

无法强制 PowerShell 使用特定的输入编码。 PowerShell 5.1 及更低版本,在 Windows 上运行,区域设置设置为 en-US,如果没有 BOM,则默认为 Windows-1252 编码。 其他区域设置可能使用不同的编码。 为了确保互操作性,最好使用 BOM 以 Unicode 格式保存脚本。

重要

接触 PowerShell 脚本的任何其他工具都可能会受到编码选择的影响,或者将脚本重新编码为另一种编码。

现有脚本

文件系统上已有的脚本可能需要重新编码为新的所选编码。 在 VS Code 的底部栏中,你将看到标签 UTF-8。 单击它可打开操作栏,然后选择“保存 编码。 现在可以为该文件选择新的编码。 有关完整说明,请参阅 VS Code 的编码

如果需要重新编码多个文件,可以使用以下脚本:

Get-ChildItem *.ps1 -Recurse | ForEach-Object {
    $content = Get-Content -Path $_
    Set-Content -Path $_.Fullname -Value $content -Encoding UTF8 -PassThru -Force
}

PowerShell 集成脚本环境 (ISE)

如果还使用 PowerShell ISE 编辑脚本,则需要在那里同步编码设置。

ISE 应遵循 BOM,但也可以使用反射来 设置编码。 请注意,这不会在启动之间保留。

源代码管理软件

某些源代码管理工具(如 git)忽略编码;git 只跟踪字节。 其他项目(如 Azure DevOps 或 Mercurial)可能不会。 即使是一些基于 git 的工具也依赖于解码文本。

如果是这种情况,请确保:

  • 在源代码管理中配置文本编码以匹配 VS Code 配置。
  • 确保在相关编码中将所有文件签入源代码管理。
  • 对通过源代码管理收到的编码的更改保持谨慎。 此键符号是一个差异,指示更改,但似乎没有任何变化(因为字节有但字符没有)。

协作者的环境

在配置源代码管理的基础上,请确保共享的任何文件上的协作者没有通过重新编码 PowerShell 文件替代编码的设置。

其他程序

读取或写入 PowerShell 脚本的任何其他程序均可重新编码。

一些示例包括:

  • 使用剪贴板复制和粘贴脚本。 这种情况在以下情况下很常见:
    • 将脚本复制到 VM
    • 从电子邮件或网页复制脚本
    • 将脚本复制到 Word 或 PowerPoint 文档Microsoft或传出
  • 其他文本编辑器,例如:
    • 记事本
    • vim
    • 任何其他 PowerShell 脚本编辑器
  • 文本编辑实用工具,例如:
    • Get-Content/Set-Content/Out-File
    • PowerShell 重定向运算符,例如 >>>
    • sed/awk
  • 文件传输程序,例如:
    • 下载脚本时 Web 浏览器
    • 文件共享

其中一些工具以字节而不是文本形式处理,但其他工具提供编码配置。 在需要配置编码的情况下,需要使其与编辑器编码相同,以防止出现问题。

在 PowerShell 中编码的其他资源

在 PowerShell 中,还有一些关于编码和配置编码的其他好文章值得阅读: