PowerShell 技能连载 - 用 PowerShell 操作 Chocolatey

Chocolatey 是一个 Windows 平台上免费的包管理器,可以用来下载和安装软件。

在用 PowerShell 操作 Chocolatey 之前,您需要下载和安装它。如果您没有管理员特权,请使用以下代码。它将下载安装脚本,检测它的数字签名并确保它是合法的,然后运行它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# download and save installation script, then check signature
$url = 'https://chocolatey.org/install.ps1'
$outPath = "$env:temp\installChocolatey.ps1"
Invoke-WebRequest -UseBasicParsing -Uri $url -OutFile $outPath

# test signature
$result = Get-AuthenticodeSignature -FilePath $outPath
if ($result.Status -ne 'Valid')
{
Write-Warning "Installation Script Damaged/Malware?"
exit 1
}

# install chocolatey for current user
$env:ChocolateyInstall='C:\ProgramData\chocoportable'

Start-Process -FilePath powershell -ArgumentList "-noprofile -noExit -ExecutionPolicy Bypass -File ""$outPath""" -Wait

# clean up
Remove-Item -Path $outPath

要查看更多的安装选项,请访问 https://chocolatey.org/install

以上脚本不需要管理员权限,以便携应用的方式安装 Chocolatey。要使用它,您需要临时将安装文件夹添加到 Windows 的 Path 环境变量。然后输入 choco 来测试安装情况。该命令将汇报它的版本号:

1
2
3
4
5
6
7
PS> $env:path += ";C:\ProgramData\chocoportable"

PS> choco
Chocolatey v0.10.8
Please run 'choco -?' or 'choco -?' for help menu.

PS>

我们将看看如何使用 Chocolatey 自动下载和安装 PowerShell Core 6,然后和您当前 Windows PowerShell 安装并行地使用它。

同时,访问 https://chocolatey.org/packages?q=notepadplusplus 来查看您是否可以通过 Chocolatey 来安装。以下是使用 Chocolatey 需要考虑的关键步骤:

  • 使用 PowerShell 控制台窗口。不要使用没有真正控制台窗口的 PowerShell 宿主(例如 ISE)
  • 如果可能,使用管理员权限运行 PowerShell。许多包需要完整的 Administrator 权限来安装软件
  • 将 Chocolatey 安装路径添加到 Path 环境变量

当您的环境准备好后,可以试试下载和安装一个工具,例如 Notepad++ 是多么简单:

PS C:\> $env:path += ";C:\ProgramData\chocoportable"
PS C:\> choco install notepadplusplus -y
Chocolatey v0.10.8
[Pending] Removing incomplete install for 'notepadplusplus'
Installing the following packages:
notepadplusplus
By installing you accept licenses for the packages.

notepadplusplus.install v7.5.6 [Approved]
notepadplusplus.install package files install completed. Performing ot
her installation steps.
Installing 64-bit notepadplusplus.install...
notepadplusplus.install has been installed.
notepadplusplus.install installed to 'C:\Program Files\Notepad++'
Added C:\ProgramData\chocoportable\bin\notepad++.exe shim pointed to '
c:\program files\notepad++\notepad++.exe'.
  notepadplusplus.install may be able to be automatically uninstalled.

 The install of notepadplusplus.install was successful.
  Software installed as 'exe', install location is likely default.

notepadplusplus v7.5.6 [Approved]
notepadplusplus package files install completed. Performing other inst
allation steps.
 The install of notepadplusplus was successful.
  Software install location not explicitly set, could be in package or

  default install location if installer.

Chocolatey installed 2/2 packages.
 See the log for details (C:\ProgramData\chocoportable\logs\chocolatey
.log).
PS C:\>

当 Chocolatey 完成安装 Notepad++ 的安装后,只需要按下 Win + R 键,然后在“运行”对话框中输入

Notepad++

编辑器就启动了。

PowerShell 技能连载 - 检查数字签名

当您从 internet 下载一个脚本时,它可能包含了一个数字签名,数字签名能帮您确定脚本是从哪里来的。我们在前一个技能里讨论了这个内容,以下是我们使用的代码:它将一个 PowerShell 脚本下载到磁盘,然后显示它的数字签名:

1
2
3
4
5
6
7
# save script to file
$url = 'https://chocolatey.org/install.ps1'
$outPath = "$env:temp\installChocolatey.ps1"
Invoke-WebRequest -UseBasicParsing -Uri $url -OutFile $outPath

# test signature
Get-AuthenticodeSignature -FilePath $outPath

结果类似这样:

    Directory: C:\Users\tobwe\AppData\Local\Temp


SignerCertificate                         Status         Path
-----------------                         ------         ----
493018BA27EAA09B895BC5660E77F694B84877C7  Valid          installChocolatey.ps1

“Status” 列报告了这个文件是否可信。然而如何获取更多的关于证书和它的所有者的信息,特别是找出 “493018BA27EAA09B895BC5660E77F694B84877C7” 是谁?

只需要将签名证书传给一个 Windows API 函数,就可以显示证书的属性对话框:

1
2
3
4
5
6
7
8
9
10
11
# save script to file
$url = 'https://chocolatey.org/install.ps1'
$outPath = "$env:temp\installChocolatey.ps1"
Invoke-WebRequest -UseBasicParsing -Uri $url -OutFile $outPath

# test signature
$result = Get-AuthenticodeSignature -FilePath $outPath
$signerCert = $result.SignerCertificate

Add-Type -Assembly System.Security
[Security.Cryptography.x509Certificates.X509Certificate2UI]::DisplayCertificate($signerCert)

现在您能了解到该证书编号指向 “Chocolatey Software, Inc” 公司,以及该证书是由 DigiCert 颁发。这是为什么 Windows 信任该签名:DigiCert 采取措施验证签名人的个人详细信息。

PowerShell 技能连载 - 信任下载的文件

通过 Internet 下载的脚本,很有可能被恶意软件感染过,或者源头是法非的数据来源。数字签名可以增加一个额外的信任和保护层。

作为示例,我们将测试官方的 “Chocolatey” 安装脚本,它的下载地址在这里:

https://chocolatey.org/install.ps1

当您在浏览器中打开这个 URL 时,您将会看到一个十分长的 PowerShell 脚本,您现在需要十分谨慎地检查每一行代码,在运行它之前确保它是完整的,并且不会作恶。

幸运的是,在脚本的尾部您会发现一个非常长的注释块。这是一个数字签名。要检查是否能够信任该脚本,以及它是否未被篡改过,您需要将该代码保存到一个文件中。然后,您可以验证签名:

1
2
3
4
5
6
7
# save script to file
$url = 'https://chocolatey.org/install.ps1'
$outPath = "$env:temp\installChocolatey.ps1"
Invoke-WebRequest -UseBasicParsing -Uri $url -OutFile $outPath

# test signature
Get-AuthenticodeSignature -FilePath $outPath

结果看起来类似这样:

    Directory: C:\Users\tobwe\AppData\Local\Temp


SignerCertificate                         Status         Path
-----------------                         ------         ----
493018BA27EAA09B895BC5660E77F694B84877C7  Valid          installChocolatey.ps1

如果 “Status” 列报告 “Valid”,那么说明:

  • 这个文件未被篡改过,是原始的内容
  • 该文件是由 “SignerCertificate” 报告的证书创建的

当然您不能理解 “493018BA27EAA09B895BC5660E77F694B84877C7” 是谁,但您可以确定 Windows 认为该证书是可信任的,,所以您可以十分安全地运行这个脚本(如果您想了解 493018BA27EAA09B895BC5660E77F694B84877C7 到底是谁,请看明天的技能)。

以下是 “Status” 其它可能的情况:

  • HashMismatch:文件内容被修改。这种情况高度可疑。
  • Unknown:签名的证书不被信任。任何人都有可能签名了这个文件。这个签名对您没有任何意义。
  • NotSigned:该脚本没有签名。

如果 Status 的值不是 Valid,那么该签名对您没有任何意义,在运行之前必须人工检查和测试代码。

如果 Status 的值是 Valid,那么您可以明确地确定创建脚本的人,而且您可以安全地认为它没有被其他人改过。不过,一个合法的签名并不是完全地保证,说明该脚本是无害的。

PowerShell 技能连载 - 执行策略和下载的脚本文件

当您从 internet 下载了一个文件,它可能会被 Windows 标记(通过 NTFS 流),并且 PowerShell 可能会拒绝执行它:

1
2
3
4
5
6
7
8
9
PS> & "$home\desktop\Rick.ps1"
& : File C:\Users\tobwe\desktop\Rick.ps1 cannot be loaded. The file C:\Users\tobwe\desktop\Rick.ps1 is not digitally signed. You cannot run this script on the
current system. For more information about running scripts and setting execution policy, see about_Execution_Policies at
https:/go.microsoft.com/fwlink/?LinkID=135170.
At line:1 char:3
+ & "$home\desktop\Rick.ps1"
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : SecurityError: (:) [], PSSecurityException
+ FullyQualifiedErrorId : UnauthorizedAccess

通常,当执行策略没有设置,或者设成 “RemoteSigned” 的时候会出现这种情况。这是普通 PowerShell 用户推荐的设置。以下是启用设置的方法:

1
PS> Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned

当启用以后,您可以运行任何本地脚本文件,或域之内的网络文件,但您不再能运行标记为“下载”的脚本,或从您域之外的网络位置下载的脚本。

要运行屏蔽的脚本,以下是您的选项:

  • 取消对该文件的屏蔽,主要是通过打开它的属性对话框,单击“解除锁定”
  • 使用 Unblock-File
  • 将文件内容拷贝到一个新的文件
  • 将执行策略设为 “Bypass”
  • 用不会标记下载文件的浏览器来下载,或用 Invoke-WebRequest 来下载:
1
PS> Invoke-WebRequest -Uri "http://bit.ly/e0Mw9w" -UseBasicParsing -OutFile  "$home\Desktop\Rick.ps1"

Invoke-WebRequest 不会对下载的文件做标记,而且允许通过执行策略,这是挺令人意外的行为。

PowerShell 技能连载 - 下载脚本文件的最佳方式

有时候,PowerShell 脚本的作者将脚本放在直接下载的服务器上。让我们寻找一种最有效的通过 PowerShell 下载文本文件的方法。我们将以 PowerShell Team 成员 Lee Holmes 发布的著名的 “Dancing Rick ASCII” 脚本作为我们的例子。它的下载地址位于这里(需要翻墙):

http://bit.ly/e0Mw9w

当在浏览器中打开时,您将会见到以纯文本方式显示的 PowerShell 源代码,并且原始的 URL 将会显示在浏览器的地址栏里:

http://www.leeholmes.com/projects/ps_html5/Invoke-PSHtml5.ps1

许多用户像这样使用 .NET 方法来下载文本文件:

1
2
3
4
5
6
7
# download code
$url = "http://bit.ly/e0Mw9w"
$webclient = New-Object Net.WebClient
$code = $webclient.DownloadString($url)

# output code
$code

其实并不需要这样,因为 Invoke-WebRequest 是对该对象的更好的封装:

1
2
3
4
# download code
$url = "http://bit.ly/e0Mw9w"
$page = Invoke-WebRequest -Uri $url -UseBasicParsing
$code = $page.Content

通过它的参数,它能直接支持代理并且支持凭据。

还有一个更方便的 cmdlet Invoke-RestMethod。它基本上做的是相同的是,不过返回的数据是文本,JSON,或 XML:

1
2
3
# download code
$url = "http://bit.ly/e0Mw9w"
$code = Invoke-RestMethod -Uri $url -UseBasicParsing

假设您信任这段代码,相信它不会损害您的系统,您可以这样执行它:

1
2
# invoke the code
Invoke-Expression -Command $code

或者,您可以将它保存到磁盘,并且以一个普通的 PowerShell 脚本的方式执行它:

1
2
3
4
5
6
7
8
# download code
$url = "http://bit.ly/e0Mw9w"
$code = Invoke-RestMethod -Uri $url -UseBasicParsing

# save to file and run
$outPath = "$home\Desktop\dancingRick.ps1"
$code | Set-Content -Path $outPath -Encoding UTF8
Start-Process -FilePath powershell -ArgumentList "-noprofile -noexit -executionpolicy bypass -file ""$outPath"""

如果您打算先将内容保存到一个文件,那么 Invoke-WebRequest 是一个更好的选择,因为它可以直接将内容保存到文件:

1
2
3
4
5
# download code
$url = "http://bit.ly/e0Mw9w"
$outPath = "$home\Desktop\dancingRick.ps1"
$code = Invoke-WebRequest -Uri $url -UseBasicParsing -OutFile $outPath
& $outPath

您可以通过调用操作符 (&) 在您自己的 PowerShell 会话中运行下载的文件,而不是使用 Start-Process。如果执行失败,通常是因为您的执行策略不允许 PowerShell 脚本。请按如下方法改变设置,然后重试:

1
PS> Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned

PowerShell 技能连载 - 对 Cmdlet 的输出着色

从 PowerShell 5.1 开始,PowerShell 控制台支持 VT 转义序列,它可以用于对控制台文本定位和格式化。请注意它只对控制台有效,而对 PowerShell ISE 无效。另外还请注意您需要 Windows 10 或者类似 ConEmu 等模拟器。

当您向 Select-Object 命令传入一个哈希表时,该哈希表能够产生“计算的”列。它提供了两块信息:名字(列名)和表达式(一个生成列内容的脚本块)。

这对为 cmdlet 的输出着色十分有用。只需要创建一个添加彩色的 VT 转义序列的表达式即可。在以下例子中,一些文件类型被着色:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ColoredName = @{
Name = "Name"
Expression =
{
switch ($_.Extension)
{
'.exe' { $color = "255;0;0"; break }
'.log' { $color = '0;255;0'; break }
'.ini' { $color = "0;0;255"; break }
default { $color = "255;255;255" }
}
$esc = [char]27
"$esc[38;2;${color}m$($_.Name)${esc}[0m"
}
}

Get-ChildItem $env:windir |
Select-Object -Property Mode, LastWriteTime, Length, $ColoredName

PowerShell 技能连载 - 在 PowerShell 控制台中使用颜色

从 PowerShell 5.1 开始,PowerShell 控制台支持 VT 转义序列,它可以用于对控制台文本定位和格式化。请注意它只对控制台有效,而对 PowerShell ISE 无效。另外还请注意您需要 Windows 10 或者类似 ConEmu 等模拟器。

要对一段文字着色,您在任何 PowerShell 版本中都可以使用 Write-Host 和它的 -ForegroundColor-BackgroundColor 属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
foreach($color1 in (0..15))
{
foreach($color2 in (0..15))
{
Write-Host -ForegroundColor ([ConsoleColor]$color1) -BackgroundColor ([ConsoleColor]$color2) -Object "X" -NoNewline
}

Write-Host
}



XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXX

通过使用 VT 转义序列,您可以使用范围更广的颜色。可以将前景色和背景色设为任意的 RGB 颜色。每个通道可以有 8 比特 (0-255):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Red/Green/Blue foreground
$r = 0
$g = 20
$b = 255

# Red/Green/Blue background
$rback = 255
$gback = 100
$bback = 0

$esc = [char]27

# compose escape sequence
"$esc[38;2;$r;$g;$b;48;2;$rback;$gback;${bback}mCOLORFUL TEXT$esc[0m"

以下代码创建所有可能的颜色,并且将它们显示在由 256 个字符组成的一行上,并且每一行叠加在前一行之上,这样可以获得一个渐变发光的效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
$esc = [char]27
$setCursorTop = "$esc[0;0H"

foreach($rback in (0..255))
{
foreach($gback in (0..255))
{
foreach($bback in (0..255))
{

foreach($r in (0..255))
{
foreach($g in (0..255))
{
[System.Text.StringBuilder]$line = ""
foreach($b in (0..255))
{
$null = $line.Append("$esc[38;2;$r;$g;$b;48;2;$rback;$gback;${bback}mX$esc[0m")
}

$text = $line.ToString()
Write-Host "$setCursorTop$Text"
}

Write-Host
}
}
}
}

PowerShell 技能连载 - PowerShell 控制台光标定位

从 PowerShell 5.1 开始,PowerShell 控制台支持 VT 转义序列,它可以用于对控制台文本定位和格式化。请注意它只对控制台有效,而对 PowerShell ISE 无效。另外还请注意您需要 Windows 10 或者类似 ConEmu 等模拟器。

VT 转义序列可以将控制台光标设置到控制台窗口的任意位置。例如,要将光标设置到左上角,请使用以下代码:

1
2
3
4
$esc = [char]27
$setCursorTop = "$esc[0;0H"

Write-Host "${setCursorTop}This always appears in line 0 and column 0!"

当您运行这段代码时,文字总是定位在第 0 行 第 0 列。您可以使用这种技术来创建您自己的进度指示器——只需要记住:这一切只在控制台窗口中有效,在 PowerShell ISE 中无效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function Show-CustomProgress
{
try
{
$esc = [char]27

# let the caret move to column (horizontal) pos 12
$column = 12
$resetHorizontalPos = "$esc[${column}G"
$gotoFirstColumn = "$esc[0G"

$hideCursor = "$esc[?25l"
$showCursor = "$esc[?25h"
$resetAll = "$esc[0m"

# write the template text
Write-Host "${hideCursor}Processing %." -NoNewline

1..100 | ForEach-Object {
# insert the current percentage
Write-Host "$resetHorizontalPos$_" -NoNewline
Start-Sleep -Milliseconds 100
}
}
finally
{
# reset display
Write-Host "${gotoFirstColumn}Done. $resetAll$showCursor"
}
}

运行这段代码后,执行 Show-CustomProgress 命令,您将会见到一个不断增长的自定义进度指示器。控制台隐藏了闪烁的光标提示。当进度指示器结束时,或者当按下 CTRL + C 时,进度指示器将会隐藏,

PowerShell 技能连载 - 在同一行输出日志信息

从 PowerShell 5.1 开始,PowerShell 控制台支持 VT 转义序列,它可以用于对控制台文本定位和格式化。请注意它只对控制台有效,而对 PowerShell ISE 无效。另外还请注意您需要 Windows 10 或者类似 ConEmu 等模拟器。

VT 转义序列可以将控制台光标设置到当前行的任意位置。通过这种方式,您可以方便地创建一个函数,输出状态或者日志信息到控制台。并且每条新信息覆盖之前的信息而不是增加新的行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Write-ConsoleMessage([string]$Message)
{
$esc = [char]27
$consoleWidth = [Console]::BufferWidth
$outputText = $Message.PadRight($consoleWidth)
$gotoFirstColumn = "$esc[0G"
Write-Host "$gotoFirstColumn$outputText" -NoNewline
}

function test
{
Write-ConsoleMessage -Message 'Starting...'
Start-Sleep -Seconds 1
Write-ConsoleMessage -Message 'Doing something!'
Start-Sleep -Seconds 1
Write-ConsoleMessage -Message 'OK.'
}

test

PowerShell 技能连载 - 为控制台输出加下划线

从 PowerShell 5.1 开始,PowerShell 控制台支持 VT 转义序列,它可以用于对控制台文本定位和格式化。请注意它只对控制台有效,而对 PowerShell ISE 无效。另外还请注意您需要 Windows 10 或者类似 ConEmu 等模拟器。

要实验这个功能,请在 PowerShell 控制台中运行以下代码:

1
2
$esc = [char]27
"$esc[4mOutput is now underlined!"

PowerShell 现在对所有的输出加下划线。输入文本并没有加下划线(由于 PSReadLine 处理所有输入文本的格式)。

要关闭格式化功能,请运行这段代码:

1
2
$esc = [char]27
"$esc[0mReset"

我们将会在未来的技能中介绍更多的控制台文字格式化技术。以下是 VT 转义序列的更深入介绍:https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences.