PowerShell 技能连载 - 操作 PowerShell Gallery 内容

公共的 PowerShell Gallery (www.powershellgallery.com) 是一个 PowerShell 脚本作者们可以自由地交换脚本和模块的地方。您所需要的只是包含 PowerShellGet 的 PowerShell 5,它带了操作 Gallery 需要的 cmdlet。

当然,您需要自己确认代码,确保它可以完美运行,并且没有安全问题。

有一个模块叫做“PSCredentialManager”。以下是如何查看这个模块信息的方法:

1
2
3
4
5
6
7
8
9
10
11
12
PS> Find-Module -Name PSCredentialManager | Select-Object -Property *


Name : pscredentialmanager
Version : 1.0.1
Type : Module
Description : This module allows management and automation of Windows cached credentials.
Author : Adam Bertram
CompanyName : adamtheautomator
Copyright : (c) 2017 Adam Bertram. All rights reserved.
PublishedDate : 18.06.2017 22:14:27
...

要安装这个模块,请运行以下代码:

1
PS> Install-Module -Name PSCredentialManager -Scope CurrentUser -RequiredVersion 0.6

请注意我们显式地要求安装 0.6 版。在写这篇文章的时候,还有个 1.0.1 版,但存在一些问题。当您从一个公开的地方,例如 PowerShell Gallery 中获取内容,随时有可能得到非预期的结果,请留意。

如果您希望在安装一个模块前检查它的代码,可以使用 Save-Module,将模块下载到一个隔离的地方。

1
2
3
4
5
6
7
PS> Find-Module pscredentialmanager -AllVersions

Version Name Repository Description
------- ---- ---------- -----------
1.0.1 pscredentialmanager PSGallery This module allows
0.6 pscredentialmanager PSGallery This module allows
0.2 pscredentialmanager PSGallery This module allows

安装完成后,以下命令可以返回一个新 cmdlet 的列表:

1
2
3
4
5
6
7
PS> Get-Command -Module pscredentialmanager

CommandType Name Version Source
----------- ---- ------- ------
Function Get-CachedCredential 0.6 pscredentialmanager
Function New-CachedCredential 0.6 pscredentialmanager
Function Remove-CachedCredential 0.6 pscredentialmanager

您现在可以管理缓存的凭据。例如要返回一个缓存的凭据列表,请试试以下代码:

1
2
3
4
5
6
7
PS> Get-CachedCredential


Name : SSO_POP_User
Category : MicrosoftAccount
Type : Domain Extended Credentials
(...)

如果您想看看新的 cmdlet(也叫函数)如何工作,您还可以阅读它的源码。这行代码将函数的源码复制到剪贴板:

1
PS> ${function:Get-CachedCredential} | clip

要弃用这个模块,只需要卸载它:

1
PS> Uninstall-Module -Name pscredentialmanager -AllVersions

PowerShell 技能连载 - 计算文件夹大小

Measure-Object 也可以计算属性值的总和。它可以用来计算文件夹大小。以下代码计算用户配置文件(可能需要一些时间,视找到的文件数量而定)。它只是将所有文件的“Length”属性相加:

1
2
3
4
$size = (Get-ChildItem -Path $home -Force -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum

"Folder Size: $sum Bytes"
'Folder Size: {0:n2} MB' -f ($size/1MB)

输出的结果类似如下:

Folder Size: 172945767402 Bytes
Folder Size: 164.933,94 MB

您可为 Get-ChildItem 添加更多的参数,显式地控制哪些文件参加统计。例如这段代码添加了 -Filter 参数,并指定文件的扩展名,来只统计用户配置文件目录中找到的 PowerShell 脚本文件的大小。

1
2
3
4
$size = (Get-ChildItem -Path $home -Filter *.ps1 -Force -Recurse -ErrorAction SilentlyContinue | Measure-Object -Property Length -Sum).Sum

"Folder Size: $size Bytes"
'Folder Size: {0:n2} MB' -f ($size/1MB)

PowerShell 技能连载 - 高效统计文件数量(第二部分)

在前一个技能中我们学习了如何有效地统计一个文件夹中项目的数量。以下是更多的例子。

用 PowerShell 统计指定文件夹中文件的数量易如反掌:

1
2
3
4
5
$count = Get-ChildItem -Path "$home\Desktop" -Force |
Measure-Object |
Select-Object -ExpandProperty Count

"Number of files: $Count"

只需要调整 Get-ChildItem 的参数就可以找到更多。例如添加 -Recurse 开关,就可以包括子文件夹中的文件:

1
2
3
4
5
$count = Get-ChildItem -Path "$home\Desktop" -Force -Recurse -ErrorAction SilentlyContinue  |
Measure-Object |
Select-Object -ExpandProperty Count

"Number of files: $Count"

或者,只关注某些文件。这个例子只统计两层目录深度以内的 log 和 txt 文件:

1
2
3
4
5
$count = Get-ChildItem -Path $env:windir -Force -Recurse -Include *.log, *.txt -ErrorAction SilentlyContinue -Depth 2 |
Measure-Object |
Select-Object -ExpandProperty Count

"Number of files: $Count"

(请注意:-Depth 参数是 PowerShell 5 引入的)

PowerShell 技能连载 - 高效统计文件数量(第一部分)

一个快速但浪费的统计文件数量的方法如下:

1
(Get-ChildItem -Path c:\windows).Count

但是,这将产生一些内存负担,因为在 Count 属性能够获取对象数量之前,所有文件将会堆在内存里。当进行递归搜索时,这种情况更严重。

一个节约非常多资源的方法是类似这样使用 Measure-Object

1
(Get-ChildItem -Path c:\windows | Measure-Object).Count

这里使用流来获取项目的数量,所以 PowerShell 不需要在内存中存储所有文件。

PowerShell 技能连载 - 还原 TinyUrl 的真实地址

URL 缩短功能对于 Twitter 消息十分有用,但是隐藏了真实地址。您是否真的信任 http://bit.ly/e0Mw9w 呢?

以下是一个简单的方法,帮您还原缩短后的 URL 指向的真实地址:

1
2
3
4
5
6
$shortUrl = "http://bit.ly/e0Mw9w"

$longURL = Invoke-WebRequest -Uri "http://untiny.me/api/1.0/extract?url=$shortUrl&format=text" -UseBasicParsing |
Select-Object -ExpandProperty Content

"'$shortUrl' -> '$longUrl'"

如您所见,这个例子中缩短的 URL 指向的是 Lee Holmes 的博客:http://www.leeholmes.com/projects/ps_html5/Invoke-PSHtml5.ps1 。Lee Holmes 是一个 PowerShell 团队成员,如果您信任他,那么可以好奇地运行他著名的代码片段:

1
iex (New-Object Net.WebClient).DownloadString("http://bit.ly/e0Mw9w")

这是一个能说明 Invoke-Expression 别名为“iex”有危险的很好例子。

PowerShell 技能连载 - 创建彩色的天气报告

在前一个技能中我们介绍了如何用 Invoke-WebRequest 来获取天气预报数据。获取到的数据是纯黑白的文本。

要获取彩色的报告,Windows 10 可以利用 powershell.exe 的控制序列特性。只需要在 PowerShell 控制台中运行以下代码:

1
2
3
$City = 'Hannover'

(Invoke-WebRequest "http://wttr.in/$City" -UserAgent curl).content -split "`n"

只需要将 user agent 设为“curl”,Windows 10 powershell.exe 就能收到包含色彩控制序列的输出。

PowerShell 技能连载 -获取天气预报

Invoke-WebRequest 可以轻松地获得网页内容。如果不指定 –UseBasicParsing 参数,HTML 内容会被解析成 Internet Explorer DOM。通过这种方法,PowerShell 只需要几行代码就可以获取世界上大多数城市当前的天气预报:

1
2
3
4
5
6
$City = 'New York'
$weather = Invoke-WebRequest -Uri "http://wttr.in/$City"
$text = $weather.ParsedHtml.building.outerText

$text
$text | Out-GridView

要注意一些事情:

  • Out-GridView 只能显示有限行的文本。要查看完整的报告,请将 $text 输出到控制台。
  • Invoke-WebRequest 需要内置的 Windows 浏览器运行和初始化至少一次
  • 以上代码是脆弱的:一旦网站作者对页面内容重新布局,脚本可能就不能工作了

PowerShell 技能连载 - 避免使用 Read-Host

您是否使用 Read-Host 来接受用户的输入?如果您是这么做的,请重新考虑一下。Read-Host 总是提示用户,并且用了 Read-Host 就无法自动化运行脚本:

1
$City = Read-Host -Prompt 'Enter City'

一个更好的简单方法是这样:

1
2
3
4
param
(
[Parameter(Mandatory)]$City
)

它创建了一个必选参数。如果用户没有提供传输这个实参,那么会创建一个提示,效果类似 Read-Host。但是,用户始终可以传入这个脚本的参数并使脚本自动化运行。只需要确保只有一个 param() 语句,并且必须放在脚本的开始处。逗号来分隔多个参数。

如果您不喜欢 param() 创建的提示,那么可以用这种方法:

1
2
3
4
param
(
$City = (Read-Host -Prompt 'Enter City')
)

这样,您可以完全控制提示的内容,并且用户又可以通过实参传入参数,并且无人值守运行脚本。

PowerShell 技能连载 - 使用在线帮助

PowerShell 的发行版并没有带帮助文件,而本地安装帮助文件需要管理员权限。

更简单的获取帮助的方法是访问 cmdlet 的在线帮助,类似这样:

1
Get-Help -Name Get-Acl -Online

这将启动一个浏览器并导航到在线帮助。在线帮助通常内容更新并且更容易获得。当然,它需要 internet 连接,并且并不是每个 cmdlet 都有在线帮助版本。

PowerShell 技能连载 - 同时输出和赋值

在前一个技能中我们介绍了如何记录脚本结果,以及如何使用括号来同时输出和赋值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PS> ($a = Get-Process -Id $pid)

Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
1595 102 283200 325444 64,56 6436 1 powershell_ise



PS> $a

Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
1595 102 283200 325444 64,75 6436 1 powershell_ise



PS>

还可以用 -OutVariable 通用参数来实现相同的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PS> Get-Process -Id $pid -OutVariable b

Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
1731 105 290336 341688 66,66 6436 1 powershell_ise



PS> $b

Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
1731 105 290336 341688 66,92 6436 1 powershell_ise



PS>

Tee-Object 是第三种方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PS> Get-Process -Id $pid | Tee-Object -Variable c

Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
1759 109 292300 343644 71,53 6436 1 powershell_ise



PS> $c

Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
1759 109 292300 343644 71,69 6436 1 powershell_ise



PS>

以上方法使用了管道,而管道的速度比较慢。如果希望提升性能,那么避免使用管道:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PS> Tee-Object -InputObject (Get-Process -Id $pid) -Variable d

Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
1761 111 294568 345268 74,31 6436 1 powershell_ise



PS> $d

Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
1761 111 294568 345268 74,59 6436 1 powershell_ise



PS>