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

问题描述 投票:6回答:3

我是Windows和Powershell noobie。我来自Linux Land。我以前在我的.bashrc中有这个小Bash功能,它会将“shruggie”(¯\_(ツ)_/¯)复制到剪贴板上,以便我可以将它粘贴到Slack等对话中。

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

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

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

function shruggie() {
  '¯\_(ツ)_/¯' | clip
  Write-Host '¯\_(ツ)_/¯ copied to clipboard.' -foregroundcolor yellow
}

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

我看过[System.Text.Encoding]::UTF8和一些other questions,但我不知道如何将我的字符串转换为UTF-8并通过clip.exe传递并在另一边(在剪贴板上)接收UTF-8。

powershell character-encoding
3个回答
6
投票

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

  • 使用¯\_(ツ)_/¯clip.exe复制到剪贴板
  • 写(回声)¯\_(ツ)_/¯到控制台

先决条件:PowerShell必须正确识别源代码的编码才能使下面的解决方案正常工作:如果源代码是UTF-8编码的,请务必将封装文件保存为带有BOM的UTF-8,以便Windows PowerShell识别它。

  • 在缺少BOM的情况下,Windows PowerShell将源解释为“ANSI”编码,指的是有效的传统单字节扩展ASCII代码页,例如美国英语系统上的Windows-1252,因此会解释UTF-8编码的源代码不正确。
  • 请注意,相比之下,PowerShell Core使用UTF-8作为默认值,因此不再需要BOM(但仍然可以识别)。

Copying ¯\_(ツ)_/¯ to the clipboard, using clip.exe:

  • 在Windows PowerShell v5.1 +中,您可以使用内置的Set-Clipboard cmdlet将文本从PowerShell中复制到剪贴板;鉴于PowerShell使用能够表示所有Unicode字符的.NET System.String类型,因此没有编码问题。 请注意,即使在Windows上运行,PowerShell Core也没有此cmdlet(从PowerShell Core v6.0.0-rc.2开始) 请参阅我的this answer,了解适用于早期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
}
  • 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

Writing ¯\_(ツ)_/¯ to the console:

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

仅仅回显(打印)Unicode字符(超出8位范围),切换到可以显示Unicode字符的字体(超出扩展的ASCII范围)就足够了,因为正如PetSerAl指出的那样,PowerShell uses the Unicode version of the WriteConsole Windows API function要打印到安慰。

要支持(大多数)Unicode字符,您最常切换到“TT”(TrueType)字体之一。

PetSerAl在一条评论中指出,Windows上的控制台窗口目前仅限于每个输出字符(单元格)一个16位代码单元;由于BMP (Basic Multilingual Plane)中只有(大多数)字符是自包含的16位代码单元,因此无法表示BMP之外的(罕见)字符。

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

实际上,从Windows 10发行版ID 1703开始,只有少数几种字体可以呈现(Unicode字符KATAKANA LETTER TUU+30C4,UTF-8:E3 83 84):

  • MS Gothic
  • NSimSum

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

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

$OutputEncoding = [console]::InputEncoding = [console]::OutputEncoding = New-Object System.Text.UTF8Encoding

上面隐含地将代码页更改为65001(UTF-8),如chcpchcp.com)中所反映的那样。

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

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


3
投票

如果您不能使用PowerShell 5的Set-Clipboard函数(这是IMO的首选解决方案),您可以以clip.exe正确理解它的方式转换/编码您的输出。

有两种方法可以实现这里的目标:

  1. Feed clip.exe with a UTF-16 fileclip < UTF16-Shruggie.txt 这里重要的部分是保存编码为的文件:Unicode(这意味着UTF-16格式little-endian byte order与BOM)
  2. 适当地编码字符串(以下部分在像ISE这样的PoSh编辑器中工作,但遗憾的是不在常规控制台中,请参阅mklment0s回答如何实现此目的):
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
function shruggie() {

  [System.Text.Encoding]::Default.GetString(
    [System.Text.Encoding]::UTF8.GetBytes('¯\_(ツ)_/¯')
) | clip.exe
  Write-Host '¯\_(ツ)_/¯ copied to clipboard.' -foregroundcolor yellow
}
shruggie

这适合我。这是一个MSDN blog post,进一步解释有关$OutputEncoding / [Console]::OutputEncoding


2
投票

帖子Set-Clipbord选项是最直接的答案,但正如PoSHv5和更高的东西所指出的那样。但是,根据OP所使用的操作系统,并非所有OS / PoSH版本都提供所有cmdlet。这并不是说Set-Clipboard不是,但由于OP说它们是新的,它只是一个抬头。

如果由于某种原因无法去那里,您可以创建自己的或使用附加模块。看这篇文章:

Convert Keith Hill's PowerShell Get-Clipboard and Set-Clipboard to a PSM1 script

使用上面帖子中的Set-Clipboard函数并修改OP的帖子以供其使用的结果:

(Get-CimInstance -ClassName Win32_OperatingSystem).Caption
Microsoft Windows Server 2012 R2 Standard

$PSVersionTable

Name                           Value                                                                                                                    
----                           -----                                                                                                                    
PSVersion                      4.0                                                                                                                      
WSManStackVersion              3.0                                                                                                                      
SerializationVersion           1.1.0.1                                                                                                                  
CLRVersion                     4.0.30319.42000                                                                                                          
BuildVersion                   6.3.9600.18773                                                                                                           
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0}                                                                                                     
PSRemotingProtocolVersion      2.2                                                                                                                      



function Set-ClipBoard 
{
    Param
    (
        [Parameter(ValueFromPipeline=$true)]
        [string] $text
    )
    Add-Type -AssemblyName System.Windows.Forms
    $tb = New-Object System.Windows.Forms.TextBox
    $tb.Multiline = $true
    $tb.Text = $text
    $tb.SelectAll()
    $tb.Copy()
}

function New-Shruggie
{
    Set-ClipBoard -text '¯\_(ツ)_/¯'
    Write-Host '¯\_(ツ)_/¯ copied to clipboard.' -foregroundcolor yellow
}

New-Shruggie

¯\_(ツ)_/¯ copied to clipboard.

Results pasted from clipboard

¯\_(ツ)_/¯

但是有一些选项,例如以下,但上述仍然是最佳途径。

首先要记住,输出由OS代码页和解释器(PoSH)控制,并且都默认为ASCII。

您可以通过查看内置变量的输出来查看PoSH默认CP设置

$OutputEncoding

根据PoSH创建者Jeffery Snover所说:我们在管道到现有可执行文件时转换为ASCII的原因是今天的大多数命令都没有正确处理UNICODE。 有些人做,大多数做不到。

所以,所有这一切......你可以改变CodePage,做像......这样的项目

[Console]::OutputEncoding

要么 ...

$OutputEncoding = New-Object -typename System.Text.UTF8Encoding

如果将输出发送到文件...

$OutPutData | Out-File $outFile -Encoding UTF8
© www.soinside.com 2019 - 2024. All rights reserved.