如何使用PowerShell将UTF-8字符传递给clip.exe而不转换为另一个字符集?

jon*_*ell 6 powershell character-encoding

我是Windows和Powershell noobie.我来自Linux Land.我曾经在我的这个小Bash函数.bashrc中将一个" shruggie "(¯\_(?)_/¯)复制到剪贴板上,以便我可以将它粘贴到Slack上的对话等等.

我的Bash别名看起来像这样: alias shruggie='printf "¯\_(?)_/¯" | xclip -selection c && echo "¯\_(?)_/¯"'

我意识到这个问题是少年,但答案确实对我有价值,因为我确信我将需要在未来的某个时刻将非UTF-8字符输出到Powershell脚本中输出.

我在PowerShell配置文件中编写了这个函数:

function shruggie() {
  '¯\_(?)_/¯' | clip
  Write-Host '¯\_(?)_/¯ copied to clipboard.' -foregroundcolor yellow
}
Run Code Online (Sandbox Code Playgroud)

但是,当我在命令行上调用它时,这给了我:( ??\_(???)_/??未知的UTF-8字符被转换为?).

我已经查看了[System.Text.Encoding]::UTF8一些其他 问题,但我不知道如何将我的字符串转换为UTF-8并将其传递到clip.exe另一端(在剪贴板上)接收UTF-8.

mkl*_*nt0 6

有两个截然不同的独立方面:

  • 复制¯\_(?)_/¯剪贴板,用clip.exe
  • 写(回显)¯\_(?)_/¯控制台

先决条件:PowerShell必须正确识别源代码的编码,以便下面的解决方案能够正常工作:如果您的源代码是UTF-8编码的,请务必将封装文件保存为UTF-8,其中包含用于Windows PowerShell的BOM以识别它.

  • 在没有BOM的情况下,Windows PowerShell将源解释为"ANSI"编码,指的是有效的传统单字节扩展ASCII代码页,例如美国英语系统上的Windows-1252,因此会解释UTF-8编码的源代码不正确.

  • 请注意,相比之下,PowerShell Core使用UTF-8作为默认值,因此不再需要BOM(但仍然可以识别).


使用以下方法复制¯\_(?)_/¯剪贴板clip.exe:

  • Windows PowerShell v5.1 +中,您可以使用内置Set-Clipboardcmdlet从PowerShell中将文本复制到剪贴板; 鉴于PowerShell使用System.String能够表示所有 Unicode字符的.NET 类型,因此没有编码问题.

    • 请注意,即使在Windows上运行,PowerShell Core也没有此cmdlet(从PowerShell Core v6.0.0-rc.2开始)
    • 有关在早期PowerShell版本和PowerShell Core中工作的剪贴板函数,请参阅我的这个答案.
  • 早期版本的Windows PowerShell PowerShell Core中,使用clip.exe是一种可行的替代方法,但其使用需要额外的工作:

function shruggie() {
  $OutputEncoding = (New-Object System.Text.UnicodeEncoding $False, $False).psobject.BaseObject
  '¯\_(?)_/¯' | clip
  Write-Verbose -Verbose "Shruggie copied to clipboard." # see section about console output
}
Run Code Online (Sandbox Code Playgroud)
  • New-Object System.Text.UnicodeEncoding $False, $False创建一个BOM- UTF16-LE编码,这clip.exe理解.

    • .psobject.BaseObject不幸的是,神奇的咒语需要解决一个bug ; 在PSv5 +中,您可以通过使用以下代码来绕过此错误:
      [System.Text.UnicodeEncoding]::new($False, $False)
  • 将该编码分配给首选项变量$OutputEncoding可确保PowerShell使用该编码将数据传输到外部实用程序clip.exe.


写入¯\_(?)_/¯控制台:

注意:Unix平台上的PowerShell Core通常使用默认编码为(无BOM)UTF-8的控制台(终端),因此不需要额外的工作.

仅仅回显(打印)Unicode字符(超出8位范围),切换到可以显示Unicode字符(超出扩展ASCII范围)的字体就足够了,因为,正如PetSerAl指出的那样,PowerShell使用Unicode版本WriteConsole要打印到控制台Windows API函数.

支持(大多数)Unicode字符,您最常切换到"TT"(TrueType)字体之一.

PetSerAl在一条评论中指出,Windows上的控制台窗口目前仅限于每个输出字符(单元格)一个16位代码单元; 鉴于仅(大部分)在所述字符BMP(基本多语种平面)是自包含的16位代码单元,超越BMP的(罕见)字符不能被表示.

遗憾的是,即使这对于某些(BMP)Unicode字符来说可能还不够,因为Unicode标准是版本化的,并且字体表示/实现可能会滞后.

事实上,与Windows 10发布ID 1703,只有少数的字体可以渲染 ?(Unicode字符KATAKANA LETTER TU,U+30C4UTF-8: E3 83 84):

  • MS Gothic
  • NSimSum

请注意,如果您想(也)更改其他应用程序解释此类输出的方式,则必须再次设置$OutputEncoding:

例如,要使PowerShell期望外部实用程序输入UTF-8 以及将UTF-8编码数据输出外部实用程序,请使用以下命令:

$OutputEncoding = [console]::InputEncoding = [console]::OutputEncoding = New-Object System.Text.UTF8Encoding
Run Code Online (Sandbox Code Playgroud)

以上隐含地将代码页更改为65001(UTF-8),如chcp(chcp.com)所示.

请注意,为了向后兼容,Windows控制台窗口仍默认为单字节,扩展ASCII旧版OEM代码页,例如437美国英语系统.

不幸的是,从v6.0.0-rc.2开始,这也适用于PowerShell Core,即使它已经切换到无BOM的UTF-8作为默认编码,也反映在$OutputEncoding.

  • @jonathanbell:我很高兴听到这个消息,但请查看我根据 PetSerAl 的反馈进行的更新。关于无 BOM UTF-8:确实如此,感谢上帝。 (2认同)
  • 哇,真是一团糟。:) (2认同)