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

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 目录。这是电子书的下载目录。不过,这个脚本还不够智能,如果该目录不存在的话需要手动创建这个目录。