PowerShell 技能连载 - 创建世界地图图像

如果您有与国家/地区有关的数据,则可能需要可视化并突出显示此地理数据。经典的数据图标在这里不起作用。

幸运的是,PowerShell 可以使用免费的在线地理图表。这是您可以试试这个函数:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
function Show-MapGraph
{
param
(
[Parameter(Mandatory,ValueFromPipeline)]
$InputObject,

[Parameter(Mandatory)]
[string]
$Property,

[string]
$Label = 'Items'
)

begin
{

$bucket = [System.Collections.ArrayList]::new()
}
process
{
$null = $bucket.Add($_)
}
end
{
$groups = $bucket | Where-Object { $_.$Property } | Group-Object -Property $Property -NoElement
$data = foreach ($group in $groups)
{
"['{0}',{1}]" -f $group.Name, $group.Count
}

$datastring = $data -join "`r`n,"

$HTMLPage = @"
google.charts.load('current', {
'packages':['geochart'],
});
google.charts.setOnLoadCallback(drawRegionsMap);

function drawRegionsMap() {
var data = google.visualization.arrayToDataTable([
['Country', '$Label'],
$datastring
]);

var options = {

colorAxis: {colors: ['#00FF00', '#004400']},
backgroundColor: '#81d4fa',
datalessRegionColor: '#AAAABB',
defaultColor: '#f5f5f5',
};

var chart = new google.visualization.GeoChart(document.getElementById('regions_div'));

chart.draw(data, options);
}
"@

$timestamp = Get-Date -Format 'HHmmss'
$OutPath = "$env:temp\Graph$timestamp.html"
$HTMLPage | Out-File -FilePath $OutPath -Encoding utf8
Start-Process $outpath
}
}

Show-MapGraph 基本原理是创建一个 HTML 网页,并通过适当的脚本调用填充它,然后显示它。您需要做的就是通过管道传入您的国家数据,并使用 -Property 指示对象的哪个属性包含国家名称。

PowerShell 技能连载 - 在选中的代码中运行 $PSScriptRoot

PowerShell 代码中的最大陷阱之一是自动变量 $PSScriptRoot,它始终代表当前脚本所在的文件夹的路径。但是,这要求 (a)当前脚本实际上已经保存到文件中,并且 (b)您正在执行整个文件,即通过按 F5 执行。

当您仅使用 F8 执行选中的代码时,即使您选择了整个代码,$PSScriptRoot 也为空,因此也会导致您选择的代码执行错误。

但是,在 PowerShell ISE 中,添加一些代码就能很容易地实现在选中的代码中启用 $PSScriptRoot。这是您需要运行的代码:

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
31
32
33
34
35
36
37
38
39
function Invoke-Selection
{
try
{
# get the selected text:
$selectedText = $psise.CurrentFile.Editor.SelectedText
# if no text was selected...
if ($selectedText.Length -eq 0) {
# ...select the line the caret is in and get this line instead:
$psise.CurrentFile.Editor.SelectCaretLine()
$selectedText = $psise.CurrentFile.Editor.SelectedText
}

# try and parse the code
$sb = [ScriptBlock]::Create($selectedText)

# get the missing variable content from the underlying file:
$currentFile = $psise.CurrentFile.FullPath
$currentFolder = Split-Path -Path $currentFile

# append the selected code with these automatic variables, and set them:
$runcode = @"
`$PSCommandPath = '$currentFile'
`$PSScriptRoot = '$currentFolder'
$selectedText
"@
# turn text into script block...
$scriptblock = [ScriptBlock]::Create($runcode)
# ...and execute it without private scope:
. $scriptblock

}
catch
{
throw $_.Exception
}

}
$null = $psise.CurrentPowerShellTab.AddOnsMenu.Submenus.Add('ExecuteSelection', {. Invoke-Selection}, 'SHIFT+F8')

该代码向 ISE 添加了一个新命令,可以通过按 CTRL+F8 来调用该命令。现在,假设您选择了一段代码,这段代码包含了 $PSScriptRoot,您若希望运行它,只需按 CTRL+F8 而不是 F8,它将正常执行。

该快捷键调用了 Invoke-Selection 函数。此函数将接受当前选择的文本,添加缺少的自动变量 $PSScriptRoot$PSCommandPath 到代码中,并根据当前脚本的当前文件路径来定义这些变量。然后执行脚本块。

这样,您现在可以调试并演示任何选中的代码,即使它包含自动变量。只需确保您将脚本保存在某个地方,以便 PowerShell 知道您的代码所在的位置。

PowerShell 技能连载 - 在 PowerShell 中粘贴多行

当您复制多行 PowerShell 代码并将其粘贴到 Shell 窗口中时,结果通常不是您所期望的。PowerShell 开始执行第一行,不会以整块的方式执行粘贴的代码。试着复制下面的代码,然后将其粘贴到 PowerShell 控制台窗口中来查看默认行为:

1
2
3
4
"Starting!"
$a = Read-Host -Prompt 'Enter something'
"Entered: $a"
"Completed"

粘贴块的每一行都是单独执行的,在每个输出行之前,可以看到命令提示符。

尽管此默认行为也可正常执行,但是如果您希望确保整个代码块作为一个整体执行,则将其嵌入大括号中,并用 “.“ 执行此脚本块。尝试复制这段代码:

1
2
3
4
5
6
. {
"Starting!"
$a = Read-Host -Prompt 'Enter something'
"Entered: $a"
"Completed"
}

当您粘贴此代码时,它会像从脚本文件中存储并加载它一样作为一个整体执行。

PowerShell 技能连载 - 恢复被浪费的硬盘空间

当软件收到更新时,它往往并不会清除之前不需要的更新。这些过期的“补丁文件”积累在 C:\Windows\installer 文件夹下,其中有许多 *.msp 文件。由于您不知道那些 *.msp 文件还会被用到,以及哪个文件可以安全地删除,所以不太容易恢复空间。除非您拥有 Administrator 特权(需要它才能处理存储在 Windows 文件夹中的数据)并使用 PowerShell。

只需要下载该模块(需要 Administrator 特权):

1
Install-Module -Name MSIPatches

下一步,以 Administrator 特权启动一个 PowerShell 控制台,并像这样查看可恢复的空间:

1
2
3
4
5
6
7
8
9
PS> Get-MsiPatch


TotalPatchCount : 19
TotalPatchSize : 0,96 GB
InstalledPatchCount : 5
InstalledPatchSize : 0,32 GB
OrphanedPatchCount : 14
OrphanedPatchSize : 3,64 GB

“Orphaned Patch Size” 可能是 0 到好几 GB 之间的任意值。在一个系统上,由于安装了 Office 2016,我恢复了 45GB 的孤儿补丁(显然没有清理已安装的更新)。

要真正清理不必要的补丁,请使用此行代码(需要管理员特权):

1
Get-OrphanedPatch | Move-OrphanedPatch -Destination C:\Backup

这样,您可以在安全的地方“隔离”补丁文件一段时间。不过,不要忘记在某个时间点清空目标文件夹。或者,您当然可以立即删除孤立的补丁。不过,无论您做什么,都要自担风险。

PowerShell 技能连载 - 下载文件

可以通过许多方法实现简单的文件下载。例如,使用 Invoke-RestMethodInvoke-WebRequest 或通过 BitsTransfer 模块。

如果您需要下载流式内容,那么需要更复杂的命令。在 Windows 机器上,您可以下载并安装 PSODownloader 模块:

1
Install-Module -Name PSODownloader -Scope CurrentUser

它为您提供了一个更简单的 cmdlet:Invoke-DownloadFile。您可以先将要下载的 URL 复制到剪贴板然后调用该命令,或者使用 -Url 参数。

在此可以查看更多信息:

https://github.com/TobiasPSP/Modules.PsoDownloader

PowerShell 技能连载 - 清理 PowerShell 模块(第 3 部分)

在第一部分和第二部分中,我们全面学习了如何删除 PowerShell 模块。在这最后一部分中,我们将研究您可能不再需要的 PowerShell 模块版本。

每当将 PowerShell 模块更新为新版本时,新版本就可以与所有旧版本并行保存。这个设计方式很聪明,因为这可以方便您更新模块,即使它们当前正在使用。

然而,这种设计也导致了越来越多的垃圾。除非您无需明确地退回到旧版本的模块,否则通常只需要最新版本的模块。

这就是为什么下面的脚本列出模块的多个可用版本,然后让用户决定应清理哪一个:

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
31
32
33
# script may require Administrator privileges if you want to remove
# module versions installed in "AllUsers" scope


# find ALL modules with more than one version and/or location:
$multiversion = Get-Module -ListAvailable |
Group-Object -Property Name |
Sort-Object -Property Name |
Where-Object Count -gt 1

# ask user WHICH of these modules to clean?
$clean = $multiversion |
Select-Object -Property @{N='Versions';E={$_.Count}}, @{N='ModuleName';E={$_.Name}} |
Out-GridView -Title 'Select module(s) to clean' -PassThru

# get the todo list with the modules the user wants to clean:
$todo = $multiversion | Where-Object Name -in $clean.ModuleName

$todo |
ForEach-Object {
$module = $_.Name
# list all versions of a given module and let the user decide which versions
# to keep and which to remove:
$_.Group |
Select-Object -Property Version, ModuleBase, ReleaseNotes |
Sort-Object -Property Version |
Out-GridView -Title "Module $module : Select all versions that you want to remove" -PassThru |
Select-Object -ExpandProperty ModuleBase |
# do a last confirmation dialog before permanently deleting the subversions:
Out-GridView -Title 'Do you really want to permanently delete these folders? CTRL+A and OK to confirm' -PassThru |
Remove-Item -Recurse -Force

}

一旦用户选择一个或多个模块要清理,脚本一次会处理一个模块,并列出其所有版本。然后,用户可以选择要删除的版本。

请注意,您可能需要管理员特权才能删除 “AllUsers” 范围中安装的模块版本。

PowerShell 技能连载 - 清理 PowerShell 模块(第 2 部分)

在第一部分中,我们研究了如何删除通过 “Install-Module“ 安装的 PowerShell 模块。如果您不再需要它们,则可以手动删除这些 PowerShell 模块。毕竟它们只是文件夹。

这段的代码列出了所有可用的 PowerShell 模块,并让您选择要删除的模块。

1
2
3
4
5
6
7
8
9
10
# folders where PowerShell looks for modules:
$paths = $env:PSModulePath -split ';'
# finding actual module folders
$modules = Get-ChildItem -Path $paths -Depth 0 -Directory | Sort-Object -Property Name

$modules |
Select-Object -Property Name, @{N='Parent';E={$_.Parent.FullName}}, FullName |
Out-GridView -Title 'Select module(s) to permanently delete' -PassThru |
Out-GridView -Title 'Do you REALLY want to remove the modules below? CTRL+A and OK to confirm' -PassThru |
Remove-Item -Path { $_.FullName } -Recurse -Force -WhatIf # remove -WhatIf to actually delete (as always at own risk)

注意:如果模块安装在 “AllUsers” 范围中,则可能需要管理员特权。

注意:删除模块时,它将从硬盘驱动器中永久删除。确保您知道它发布了哪些 cmdlet,并且确信不再需要它们。

PowerShell 技能连载 - 清理 PowerShell 模块(第 1 部分)

有很多脚本可以通过转换一系列二进制值来读取注册表中原始的 Windows 10 产品密钥。

在这个迷你系列的第一部分中,我们将查看 PowerShell 保存其模块的位置,以及您可以采取什么措施删除不再需要的模块。

最安全的方法是完全专注于通过 Install-Module 安装的模块,因为这样您永远不会意外删除 Windows 或属于其他软件产品的一部分的模块。

这行代码列出了由 Install-Module 安装的所有模块,并让您选择要(永久)删除的模块。

1
2
3
4
Get-InstalledModule |
Out-GridView -Title 'Select module(s) to permanently delete' -PassThru |
Out-GridView -Title 'Do you REALLY want to remove the modules below? CTRL+A and OK to confirm' -PassThru |
Uninstall-Module

注意:如果模块安装在 “AllUsers” 范围中,则可能需要管理员特权。

注意:删除模块时,它将从硬盘驱动器中永久删除。确保您知道它发布了哪些 cmdlet,并且确信不再需要它们。如果您不小心删除了模块,则可以随时通过 Install-Module 重新安装它。

PowerShell 技能连载 - 快速查找过期的 PowerShell 模块

在最简单的情况下,您可以仅使用单行代码(删除 -WhatIf 以实际执行更新)检查所有已安装的模块以进行更新:

1
PS C:\> Get-InstalledModule | Update-Module -WhatIf

Get-InstalledModule 列出了以“托管”方式安装的所有模块(使用 Install-Module),并包含有关该模块的安装位置的信息(即 PowerShell Gallery 网站)。这就是 Update-Module 用来检查新版本所需要的信息。

如果您只是想看看是否有模块需要更新,并且仅专注于 PowerShell Gallery 安装的模块,那么以下是检查更新的一种更快的方法:

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
31
32
33
34
35
36
37
38
function Test-GalleryModuleUpdate
{
param
(
[Parameter(Mandatory,ValueFromPipelineByPropertyName)]
[string]
$Name,

[Parameter(Mandatory,ValueFromPipelineByPropertyName)]
[version]
$Version,

[switch]
$NeedUpdateOnly

)

process
{
$URL = "https://www.powershellgallery.com/packages/$Name"
$page = Invoke-WebRequest -Uri $URL -UseBasicParsing -Maximum 0 -ea Ignore
[version]$latest = Split-Path -Path $page.Headers.Location -Leaf
$needsupdate = $Latest -gt $Version

if ($needsupdate -or (!$NeedUpdateOnly.IsPresent))
{
[PSCustomObject]@{
ModuleName = $Name
CurrentVersion = $Version
LatestVersion = $Latest
NeedsUpdate = $needsupdate
}
}
}
}

Get-InstalledModule | Where-Object Repository -eq PSGallery |
Test-GalleryModuleUpdate #-NeedUpdateOnly

Test-GalleryModuleUpdate 函数读取了 Get-InstalledModule 返回的模块,并检查在 powershellgallery.com 上是否发布了新版本。此检查是由通过解析 URL 快速完成的。如果添加 -NeedUpdateOnly switch 参数,则 Test-GalleryModuleUpdate 仅返回需要更新的模块(可能没有结果)。

这是示例输出:

ModuleName    CurrentVersion LatestVersion NeedsUpdate
----------    -------------- ------------- -----------
ImportExcel   7.5.2          7.5.3                True
PSEventViewer 1.0.17         1.0.22               True
Az.Tools.P... 0.5.0          1.0.1                True
Microsoft.... 16.0.21116.... 16.0.22413...        True
MicrosoftT... 2.3.1          4.4.1                True
PSReadLine    2.2.2          2.2.5                True
PSWriteHTML   0.0.172        0.0.174              True
...

PowerShell 技能连载 - 解锁多个文件

当您从 Internet 下载文件,或将文件从不信任源复制到 NTFS 文件系统的驱动器时,Windows 将秘密的 NTFS 流添加到这些文件中,以作为额外的安全性层。

“锁定”的文件将无法执行,并且像 DLL 这样的“锁定”二进制文件无法被加载。这就是为什么在使用此类文件之前取消锁定这些文件的原因。从本质上讲,是通过删除隐藏的 NTFS 流来完成解密,该流将文件标记为来自“不受信任的来源”。

PowerShell 用 Unblock-File cmdlet 清除文件的隐藏的 NTFS 流。要解开多个文件,即整个子文件夹的完整内容,只需使用 Get-ChildItem 并将结果通过管道输送来解开文件:

1
Get-ChildItem -Path $home\desktop -File -Recurse | Unblock-File