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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用PowerShell将UTF-8字符传递给clip.exe而不转换为另一个字符集?相关的知识,希望对你有一定的参考价值。

我是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。

答案

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

  • 使用¯\_(ツ)_/¯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中。

另一答案

如果您不能使用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

另一答案

帖子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
以上是关于如何使用PowerShell将UTF-8字符传递给clip.exe而不转换为另一个字符集?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 PowerShell 函数中进行字符串替换?

通过管道将 CMD 输出传递给 Powershell [重复]

读取 UTF-8 文件,使用 SendInput 将内容传递给其他应用程序

Powershell脚本调用表达式查看传递给调用脚本的字符串参数并在其中查找错误

通过 bat 脚本将 deluge torrentname 传递给 powershell

在命令字符串中使用&符号进行PowerShell Invoke-Expression