PowerShell 技能连载 - 对比从 PowerShell 远程处理中受到的计算机数据

PowerShell 远程处理是一个查询多台计算机的快速方法,因为 PowerShell 远程处理是并行工作的。以下是一个演示一系列有趣技术的真实案例。

目标是从两台计算机中获取正在运行的进程的列表,然后查找区别。

为了速度最快,进程列表是通过 PowerShell 远程处理和 Invoke-Command,并且结果是从两台计算机获得的。

要区分输入的数据,我们使用了 Group-Object。它通过计算机名对数据集分组。结果是一个哈希表,而计算机名是哈希表的键。

下一步,用 Compare-Object 来快速比较两个列表并查找区别:

1
2
3
4
5
6
7
8
9
10
11
12
13
# get data in parallel via PowerShell remoting
# make sure you adjust the computer names
$computer1 = 'server1'
$computer2 = 'server2'
$data = Invoke-Command { Get-Process } -ComputerName $computer1, $computer2
# separate the data per computer
$infos = $data | Group-Object -Property PSComputerName -AsHashTable -AsString
# find differences in running processes
Compare-Object -ReferenceObject $infos.$computer1 -DifferenceObject $infos.$computer2 -Property ProcessName -PassThru |
Sort-Object -Property ProcessName |
Select-Object -Property ProcessName, Id, PSComputerName, SideIndicator

查看更多

评论

PowerShell 技能连载 - 从文本生成 MD5 哈希

Get-FileHash cmdlet 可以从文件内容生成哈希值。它无法从任意文本生成哈希值。并且只适用于 PowerShell 5 及更高的版本。

以下是一个小的函数,用 .NET Framework 从任意文本生成 MD5 哈希值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Function Get-StringHash
{
param
(
[String] $String,
$HashName = "MD5"
)
$bytes = [System.Text.Encoding]::UTF8.GetBytes($String)
$algorithm = [System.Security.Cryptography.HashAlgorithm]::Create('MD5')
$StringBuilder = New-Object System.Text.StringBuilder
$algorithm.ComputeHash($bytes) |
ForEach-Object {
$null = $StringBuilder.Append($_.ToString("x2"))
}
$StringBuilder.ToString()
}

每段文本都会生成一个唯一(且短小)的哈希值,所以它可以快速地判断文本是否唯一。它也可以用来检查一大段文本是否有变更过。

1
2
3
4
PS C:\> Get-StringHash "Hello World!"
ed076287532e86365e841e92bfc50d8c
PS C:\>

查看更多

评论

PowerShell 技能连载 - 查找重复的文件

在前一个技能中我们介绍了如何用 Get-FileHash cmdlet(PowerShell 5 新增的功能)来生成脚本文件的 MD5 哈希。

哈希可以用来查找重复的文件。大体上,哈希表可以用来检查一个文件哈希是否已经发现过。以下代码检查您的用户配置文件中的所有脚本并且报告重复的文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$dict = @{}
Get-ChildItem -Path $home -Filter *.ps1 -Recurse |
ForEach-Object {
$hash = ($_ | Get-FileHash -Algorithm MD5).Hash
if ($dict.ContainsKey($hash))
{
[PSCustomObject]@{
Original = $dict[$hash]
Duplicate = $_.FullName
}
}
else
{
$dict[$hash]=$_.FullName
}
} |
Out-GridView

查看更多

评论

PowerShell 技能连载 - 创建 MD5 文件哈希

MD5 文件哈希可以唯一确定文件内容,并且可以用来检测文件内容是否唯一。在 PowerShell 5 中,有一个新的 cmdlet 可以创建文件哈希。以下代码将在您的用户配置文件中查找所有 PowerShell 脚本,并且为每个文件生成 MD5 哈希:

1
2
3
Get-ChildItem -Path $home -Filter *.ps1 -Recurse |
Get-FileHash -Algorithm MD5 |
Select-Object -ExpandProperty Hash

一个更好的方法是将哈希值关联到原始路径上:

1
2
3
4
5
6
7
Get-ChildItem -Path $home -Filter *.ps1 -Recurse |
ForEach-Object {
[PSCustomObject]@{
Hash = ($_ | Get-FileHash -Algorithm MD5).Hash
Path = $_.FullName
}
}

输出结果类似如下:

1
2
3
4
5
6
7
8
9
10
11
Hash Path
---- ----
2AE5CA30DCF6550903B994E61A714AC0 C:\Users\tobwe\.nuget\packages\Costura.Fody...
46CB505EECEC72AA8D9104A6263D2A76 C:\Users\tobwe\.nuget\packages\Costura.Fody...
2AE5CA30DCF6550903B994E61A714AC0 C:\Users\tobwe\.nuget\packages\Costura.Fody...
46CB505EECEC72AA8D9104A6263D2A76 C:\Users\tobwe\.nuget\packages\Costura.Fody...
930621EE040F82392017D240CAE13A97 C:\Users\tobwe\.nuget\packages\Fody\2.1.2\T...
39466FE42CE01CC7786D8B446C4C11C2 C:\Users\tobwe\.nuget\packages\MahApps.Metr...
2FF7910807634C984FC704E52ABCDD36 C:\Users\tobwe\.nuget\packages\microsoft.co...
C7E3AAD4816FD98443A7F1C94155954D C:\Users\tobwe\.nuget\packages\microsoft.co...
...

查看更多

评论

PowerShell 技能连载 - Creating Balloon Tips Safely

受到 MVP 同行 Boe Prox 一篇文章的启发,编写了一个精致的创建气球状提示对话框的函数。您可以在 Boe 的原始文章中找到背景信息:https://mcpmag.com/articles/2017/09/07/creating-a-balloon-tip-notification-using-powershell.aspx

您可以找到许多关于如何显示气球状提示的使用技巧,但大多数都只是一个不能操作的任务栏图标。

以下函数是基于 Boe 的点子,但可以确保不需要全局变量或任何其它会污染 PowerShell 环境的东西。当您调用 Show-BalloonTip 时,一个气球状提示将从桌面的右下角滑入。您可以点击打开这个工具提示并再次关闭它,或者取消它。当您取消它时,它的图标会留在任务栏的托盘区域。当您点击托盘图标,该气球状图标会再次显示。

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
function Show-BalloonTip
{
param
(
[Parameter(Mandatory=$true)][string]$Text,
[string]$Title = "Message from PowerShell",
[ValidateSet('Info','Warning','Error','None')][string]$Icon = 'Info'
)
Add-Type -AssemblyName System.Windows.Forms
# we use private variables only. No need for global scope
$balloon = New-Object System.Windows.Forms.NotifyIcon
$cleanup =
{
# this gets executed when the user clicks the balloon tip dialog
# take the balloon from the event arguments, and dispose it
$event.Sender.Dispose()
# take the event handler names also from the event arguments,
# and clean up
Unregister-Event -SourceIdentifier $event.SourceIdentifier
Remove-Job -Name $event.SourceIdentifier
$name2 = "M" + $event.SourceIdentifier
Unregister-Event -SourceIdentifier $name2
Remove-Job -Name $name2
}
$showBalloon =
{
# this gets executed when the user clicks the tray icon
$event.Sender.ShowBalloonTip(5000)
}
# use unique names for event handlers so you can open multiple balloon tips
$name = [Guid]::NewGuid().Guid
# subscribe to the balloon events
$null = Register-ObjectEvent -InputObject $balloon -EventName BalloonTipClicked -Source $name -Action $cleanup
$null = Register-ObjectEvent -InputObject $balloon -EventName MouseClick -Source "M$name" -Action $showBalloon
# use the current application icon as tray icon
$path = (Get-Process -id $pid).Path
$balloon.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon($path)
# configure the balloon tip
$balloon.BalloonTipIcon = $Icon
$balloon.BalloonTipText = $Text
$balloon.BalloonTipTitle = $Title
# make the tray icon visible
$balloon.Visible = $true
# show the balloon tip
$balloon.ShowBalloonTip(5000)
}

查看更多

评论

PowerShell 技能连载 - 补零

您是否曾需要将数字转换为以零开头的字符串,例如生成服务器名?只需要使用 PowerShell 的 -f 操作符:

1
2
$id = 12
'server{0:d4}' -f $id

以下是输出结果:

1
server0012

-f 操作符左边是文本模板,右边是数值。在文本模板中,用 {x} 作为右侧数值的占位符。占位符的下标从 0 开始。

要在左侧补零,使用 d(digit 的缩写)加上您需要的数字位数即可。

查看更多

评论

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
function Get-ErrorInfo
{
param
(
[Parameter(ValueFrompipeline)]
[Management.Automation.ErrorRecord]$errorRecord
)
process
{
$info = [PSCustomObject]@{
Exception = $errorRecord.Exception.Message
Reason = $errorRecord.CategoryInfo.Reason
Target = $errorRecord.CategoryInfo.TargetName
Script = $errorRecord.InvocationInfo.ScriptName
Line = $errorRecord.InvocationInfo.ScriptLineNumber
Column = $errorRecord.InvocationInfo.OffsetInLine
Date = Get-Date
User = $env:username
}
$info
}
}

这个函数使得错误处理代码更短更容易理解。如果您需要立即处理一个错误,请使用 try/catch 概念,并且确保使用 -ErrorAction 通知 cmdlet 当发生错误时立即停止:

1
2
3
4
5
6
7
8
try
{
Stop-Service -Name someservice -ErrorAction Stop
}
catch
{
$_ | Get-ErrorInfo
}

如果您希望代码完成,并且在过后检查发生了哪些错误,请使用 -ErrorAction SilentlyContinue 以及 -ErrorVariable。同时,Get-ErrorInfo 函数有很大帮助:

1
2
$result = Get-ChildItem -Path C:\Windows -Filter *.ps1 -Recurse -ErrorAction SilentlyContinue -ErrorVariable myErrors
$myErrors | Get-ErrorInfo

查看更多

评论

PowerShell 技能连载 - 远程确定启动时间点和启动以来的时间

Get-CimInstance 是一个用来获取 WMI 信息的有用的 cmdlet,因为它使用标准的 .NET DateTime 对象,而不是奇怪的 WMI datetime 格式。然而,Get-CimInstance 使用 WinRM 来进行远程访问,而传统的 Get-WmiObject 使用 DCOM 来进行远程访问。

非常古老的系统可能还没有配置为使用 WinRM 远程处理,并且可能仍然需要 DCOM。以下是演示如何使用 Get-CimInstance 和 DOM 来查询非常古老的机器的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# change computer name to a valid remote system that you
# can access remotely
$computername = 'server12'
# use DCOM for older systems that do not run with WinRM remoting
$option = New-CimSessionOption -Protocol Dcom
$session = New-CimSession -ComputerName $computername -SessionOption $option
$bootTime = Get-CimInstance -ClassName Win32_OperatingSystem -CimSession $session | Select-Object -ExpandProperty LastBootupTime
$upTime = New-TimeSpan -Start $bootTime
$min = [int]$upTime.TotalMinutes
"Your system is up for $min minutes now."
Remove-CimSession -CimSession $session

查看更多

评论

PowerShell 技能连载 - 确定启动时间点和启动以来的时间

WMI 可以告诉您系统是什么时候启动的,还可以利用这个信息计算启动以来经历的时间:

1
2
3
4
5
$bootTime = Get-CimInstance -ClassName Win32_OperatingSystem | Select-Object -ExpandProperty LastBootupTime
$upTime = New-TimeSpan -Start $bootTime
$min = [int]$upTime.TotalMinutes
"Your system is up for $min minutes now."

请注意当使用 -ComputerName 访问远程系统时,Get-CimInstance 默认使用 WinRM 远程处理。旧的系统可能没有启用 WinRM 远程处理,而仍然使用 DCOM 技术。

查看更多

评论

PowerShell 技能连载 - 用管道将信息输出到 Excel

以下是一个短小但是十分有用的函数,能够从其它 cmdlet 接收数据并发送到 Excel:

1
2
3
4
5
6
7
8
9
10
11
function Out-Excel
{
param(
$path = "$env:temp\report$(Get-Date -Format yyyyMMddHHmmss).csv"
)
$Input |
Export-Csv $path -NoTypeInformation -UseCulture -Encoding UTF8
Invoke-Item $path
}

只需要将任何数据通过管道输出至 Out-Excel。例如:

1
PS C:\> Get-Process | Out-Excel

查看更多

评论