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

PowerShell 技能连载 - 评价事件日志信息

Get-EventLog 可以访问传统的 Windows 事件日志写入的内容。可以在一个名为 ReplacementStrings 的属性中找到最有价值的信息。以下是一个使该信息可视化并且可以利用它来生成报告的方法。

在这个例子中,将获取 Windows Update 客户端写入的 ID 为 44 的事件,并且这段代码输出替换的字符串。它们将精确地告知我们何时下载了哪些更新:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Get-EventLog -LogName System -InstanceId 44 -Source Microsoft-Windows-WindowsUpdateClient |
ForEach-Object {

$hash = [Ordered]@{}
$counter = 0
foreach($value in $_.ReplacementStrings)
{
$counter++
$hash.$counter = $value
}
$hash.EventID = $_.EventID
$hash.Time = $_.TimeWritten
[PSCustomObject]$hash


}

始终确保查询一个唯一的事件 ID:对于每个事件 ID,ReplacementStrings 中的信息是唯一的,您一定不希望将不同的事件 ID 类型中的信息混在一起。

PowerShell 技能连载 - 转换奇怪的数据格式

有些时候,您会被奇怪的数据格式难住,例如在 log 文件中,它无法自动转换为 DateTime 对象。以下是一个快速的解析此类日期时间信息的方法:

1
2
3
$weirdDate = '03 12 --- 1988'

[DateTime]::ParseExact($weirdDate, 'MM dd --- yyyy', $null)

如您所见,ParseExact() 用标准的 .NET 日期和时间字符,如您所愿处理自定义日期和时间格式。以下是大小写敏感的:

yy,yyyy: Year
M, MM, MMM, MMMM: Month
d,dd,ddd,dddd: Day
H, HH: Hour (24hr clock)
h,hh: Hour (12hr clock)
m,mm: Minute
s,ss: Second

PowerShell 技能连载 - 查找所有域控制器(不依赖于模块)

在前一个技能中我们解释了如何使用 ActiveDirectory 模块和它的 cmdlet 来查找组织中的所有域控制器,或执行任何其它 LDAP 查询。

以下使用纯 .NET 方法实现相同目的。它不需要任何其它 PowerShell 模块,而且不需要事先安装 RSAT 工具。它需要您的电脑是 Active Directory 中的一个成员。

1
2
3
4
$ldapFilter = "(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))"
$searcher = [ADSISearcher]$ldapFilter

$searcher.FindAll()

这行代码返回搜索结果对象。如果您确实想查看真实的 AD 对象,请试一试:

1
$searcher.FindAll() | ForEach-Object { $_.GetDirectoryEntry() }

PowerShell 技能连载 - 查找所有域控制器

如果您安装了免费的 Microsoft RSAT tools,那么您就拥有了 ActiveDirectory 模块。以下是一个查找组织中所有域控制器的简单方法:

1
2
3
4
#requires -Module ActiveDirectory

$filter = '(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))'
Get-ADComputer -LDAPFilter $filter

基本上,您可以运行任意的 LDAP 过滤器查询。只需要选择合适的 cmdlet,例如 Get-ADComputerGet-ADUser 或最通用的 Get-ADObject

PowerShell 技能连载 - 来自 Microsoft 的免费电子书

微软正在提供关于不同主题,无限量的免费电子书。电子书可以通过 PowerShell 来下载。
https://blogs.msdn.microsoft.com/mssmallbiz/2017/07/11/largest-free-microsoft-ebook-giveaway-im-giving-away-millions-of-free-microsoft-ebooks-again-including-windows-10-office-365-office-2016-power-bi-azure-windows-8-1-office-2013-sharepo/

以下是下载这些电子书的 PowerShell 代码:

http://ligman.me/2ux8pSo

当您运行这个脚本之前,请确保创建了 c:\book 目录。这是电子书的下载目录。不过,这个脚本还不够智能,如果该目录不存在的话需要手动创建这个目录。

PowerShell 技能连载 - 将网络连接模式从私有网络切到公有网络(反之亦然)

Starting with Windows Server 2012 R2 and Windows 8.1, PowerShell ships with many useful cmdlets for client and server configuration. This comes handy as some settings can no longer be controlled via UI.
从 Windows Server 2012 R2 和 Windows 8.1 开始,随着 PowerShell 发布了许多有用的客户端和服务器配置 cmdlet。这些 cmdlet 十分趁手,因为一些设置可以不再通过 UI 来控制。

例如,要改变网络的类型,只需要以管理员身份运行以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PS> Get-NetConnectionProfile


Name : internet-cafe
InterfaceAlias : WiFi
InterfaceIndex : 13
NetworkCategory : Private
IPv4Connectivity : Internet
IPv6Connectivity : Internet




PS> Get-NetConnectionProfile | Set-NetConnectionProfile -NetworkCategory Public -WhatIf
What if: