PowerShell 技能连载 - 读取事件日志(第 3 部分)

在上一个技能中,我们鼓励您弃用 Get-EventLog cmdlet,而开始使用 Get-WinEvent——因为后者功能更强大,并且在 PowerShell 7 中不再支持前者。

Win-EventLog 相比,Get-WinEvent 的优点之一是它能够读取所有 Windows 事件日志,而不仅仅是经典事件日志。要找出这些其他事件日志的名称,请尝试以下操作:

1
2
3
4
Get-WinEvent -ListLog * -ErrorAction Ignore |
# ...that have records...
Where-Object RecordCount -gt 0 |
Sort-Object -Property RecordCount -Descending

这将返回系统上所有包含数据的事件日志的列表,并按记录的事件数进行排序。显然,诸如“系统”和“应用程序”之类的“经典”日志比较显眼,但是还有许多其他有价值的日志,例如“具有高级安全性/防火墙的 Microsoft-Windows-Windows 防火墙”。让我们检查其内容:

1
2
3
Get-WinEvent -FilterHashtable @{
LogName = 'Microsoft-Windows-Windows Firewall With Advanced Security/Firewall'
} -MaxEvents 20

由于我的系统正在使用内置防火墙,因此结果将返回有关更改防火墙规则和其他配置历史记录的详细信息。

使用不推荐使用的 Get-EventLog 将无法获得此信息。

PowerShell 技能连载 - 读取事件日志(第 2 部分)

在上一个技能中,我们鼓励您弃用 Get-EventLog cmdlet,而开始使用 Get-WinEvent——因为后者功能更强大,并且在 PowerShell 7 中不再支持前者。

让我们再次练习如何将 Get-EventLog 语句转换为 Get-WinEvent。这是我想翻译的一行代码。它从过去 48 小时内发生的系统事件日志中返回所有错误和警告:

1
$twoDaysAgo = (Get-Date).AddDays(-2)Get-EventLog -LogName System -EntryType Error, Warning -After $twoDaysAgo

这将是在所有 PowerShell 版本中均可使用的 Get-WinEvent 单行代码:

1
2
$twoDaysAgo = (Get-Date).AddDays(-2)Get-WinEvent -FilterHashtable @{
LogName = 'System' Level = 2,3 StartTime = $twoDaysAgo }

它返回相同的事件,但是速度更快。以下是您可以在哈希表中使用的其余键:

col 1 col 2 col 3
Key name Data Type Wildcards Allowed
LogName <String[]> Yes
ProviderName <String[]> Yes
Path <String[]> No
Keywords <Long[]> No
ID <Int32[]> No
Level <Int32[]> No
StartTime No
EndTime No
UserID No
Data <String[]> No

PowerShell 技能连载 - 读取事件日志(第 1 部分)

在 Windows 中,有许多事件日志,例如“系统”和“应用程序”,在 Windows PowerShell 中,使用 Get-EventLog 从这些日志中检索事件条目很简单。这种单行代码从您的系统事件日志中返回最新的五个错误事件:

1
PS> Get-EventLog -LogName System -EntryType Error -Newest 5 | Out-GridView

在 PowerShell 7 和更高版本中,cmdlet Get-EventLog 不再存在。它被使用不同语法的 Get-WinEvent 代替,并且查询条件以哈希表的形式传入:

1
2
3
4
Get-WinEvent -FilterHashtable @{
LogName = 'System'
Level = 2
} -MaxEvents 5

Level”键是一个数字值,值越小,事件越紧急。 ID 号 2 代表“错误”条目。 ID 号 3 将代表“警告”条目。要查看错误和警告,请提交一个数组:

1
2
3
4
Get-WinEvent -FilterHashtable @{
LogName = 'System'
Level = 2,3
} -MaxEvents 5

即使您正在使用 Windows PowerShell,并且不打算很快过渡到 PowerShell 7,现在还是该习惯于 Get-WinEvent 和弃用 Get-EventLog 的时候,因为新的 Get-WinEvent 自 PowerShell 3 起就可用,并确保您的代码也将在将来的 PowerShell 版本中无缝运行。

此外,Get-WinEvent 不仅可以访问一些经典的 Windows 事件日志,还可以访问所有特定于应用程序的事件。另外,与从 Get-EventLog 接收到的结果相比, Get-WinEvent 传递的结果更加完整:后者偶尔返回带有诸如“找不到事件 xyz 的描述”之类的消息的结果。Get-WinEvent 始终返回完整的消息。

PowerShell 技能连载 - 读取上次登录的用户和其他注册表值

使用 PowerShell 读取一些注册表值通常很容易:只需使用 Get-ItemProperty。此代码段读取 Windows 操作系统的才详细信息,例如:

1
2
3
4
$Path = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion"

Get-ItemProperty -Path $Path |
Select-Object -Property ProductName, CurrentBuild, ReleaseId, UBR

结果看起来像这样:

ProductName    CurrentBuild ReleaseId UBR
-----------    ------------ --------- ---
Windows 10 Pro 19042        2009      630

不幸的是,Get-ItemProperty 实际上得与 Select-Object 结合使用才理想,因为 cmdlet 总是会添加许多属性。如果您只想读取一个注册表值,即最后一个登录的用户,则始终需要将两者结合起来:

1
2
3
4
$Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI"

Get-ItemProperty -Path $Path |
Select-Object -ExpandProperty LastLoggedOnUser

另外,您可以将 Get-ItemProperty 的结果存储在变量中,并使用点号访问各个注册表值:

1
2
3
4
$Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI"

$values = Get-ItemProperty -Path $Path
$values.LastLoggedOnUser

如果此调用不能帮助您从其他用户帐户启动 Windows Terminal,那么在即将发布的技能中,我们将说明如何将应用程序转变为不再由 Windows 管理的便携式应用程序。取而代之的是,您可以用任何用户(包括高级帐户)运行它。敬请关注。

1
2
3
4
$Path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI"

$key = Get-Item -Path $Path
$key.GetValue('LastLoggedOnUser')

PowerShell 技能连载 - 恒定函数

在 PowerShell中,您可以对函数进行写保护。当您这样做时,将无法在运行的 PowerShell 会话期间更改、覆盖或删除函数。尽管可能没有明显的作用。该方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$code =
{
param
(
[string]
[Parameter(Mandatory)]
$SomeParameter
)

"I have received $SomeParameter and could now process it..."
}

$null = New-Item -Path function:Invoke-Something -Options Constant,AllScope -Value $code

# run the function like this
Invoke-Something -SomeParameter "My Data"

由于该函数现在是恒定的,因此尝试重新定义它将会失败:

1
2
3
4
5
6
# you can no longer overwrite the function
# the following code raises an exception now
function Invoke-Something
{
# some new code
}

取消该效果的唯一方法是重新启动 PowerShell 会话。恒定变量是一个更有用的方案:通过将重要数据存储在写保护变量中,可以确保它们不会因意外或有意更改。此行定义了一个写保护变量 $testserver1,其中包含一些内容:

1
Set-Variable -Name testserver1 -Value server1 -Option Constant, AllScope

PowerShell 技能连载 - 禁止错误提示

使用 cmdlet,禁止错误提示似乎很容易:只需添加 –ErrorAction Ignore 参数。

但是,事实证明,这并不能消除所有错误。它仅禁止 cmdlet 选择处理的错误。特别是与安全相关的异常仍然显示。

如果要禁止所有错误提示,可以通过在命令尾部 2>$null 将异常传递给 null:

PS> Get-Service foobar 2>$null

这适用于所有命令甚至原生方法的调用。只需将代码括在大括号中,然后通过 调用运算符来进行调用:

1
2
3
4
5
6
PS> [Net.DNS]::GetHostEntry('notPresent')
MethodInvocationException: Exception calling "GetHostEntry" with "1" argument(s): "No such host is known."

PS> & {[Net.DNS]::GetHostEntry('notPresent')} 2>$null

PS>

PowerShell 技能连载 - 查找 PowerShell 宿主参数和可执行文件

PowerShell 宿主可以使用参数启动,即您可以使用 –NoProfile 之类的参数运行 powershell.exepwsh.exe,或提交要执行的脚本的路径。

在外壳中,您始终可以查看启动此外壳程序的命令,包括其他参数:

1
2
3
$exe, $parameters = [System.Environment]::GetCommandLineArgs()
"EXE: $exe"
"Args: $parameters"

当您使用 -NoProfile 启动 powershell.exe 时,结果如下所示:

EXE: C:\WINDOWS\system32\WindowsPowerShell\v1.0\PowerShell.exe Args: -noprofile

当您传入多个参数时,$parameters 是一个数组。

PowerShell 技能连载 - 管理已安装的模块(第 2 部分)

每当您通过 Install-Module 安装新模块或通过 Update-Module 更新现有模块时,新模块版本就会并行地安装。

如果您始终使用最新版本的模块,而无需访问特定的旧版本,则可能需要查找过时的模块并将其删除:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# get all installed modules as a hash table
# each key holds all versions of a given module
$list = Get-InstalledModule |
Get-InstalledModule -AllVersions |
Group-Object -Property Name -AsHashTable -AsString

# take all module names...
$list.Keys |
ForEach-Object {
# dump all present versions...
$list[$_] |
# sort by version descending (newest first)
Sort-Object -Property Version -Descending |
# and skip newest, returning all other
Select-Object -Skip 1
} |
# remove outdated (check whether you really don't need them anymore)
Uninstall-Module -WhatIf

PowerShell 技能连载 - 管理已安装的模块(第 1 部分)

通过 Install-Module 安装新的 PowerShell 模块时,PowerShell 会记住安装位置。因此,很容易获得通过 Install-Module 安装的模块的列表:

1
2
3
4
5
6
7
8
9
10
11
PS> Get-InstalledModule

Version Name Repository Description
------- ---- ---------- -----------
2.7.1.9 ISESteroids PSGallery Extension for PowerShell ISE 3.0 and better
2.2.0 PoSHue PSGallery Script and Control your Philips Hue Bridge, Lights, Gro...
0.14.0 platyPS PSGallery Generate PowerShell External Help files from Markdown
2.4 PSOneTools PSGallery commands taken from articles published at https://power...
1.0 QRCodeGenerator PSGallery Automatically creates QR codes as PNG images for person...
1.0 ScriptBlockLoggingAnalyzer PSGallery Functions to manage PowerShell script block logging
1.0 UserProfile PSGallery This module manages user profiles on local and remote c...

由于新版本是并行安装的,因此可以搜索不再需要的旧版本:

1
2
Get-InstalledModule |
Get-InstalledModule -AllVersions

PowerShell 技能连载 - 修复 PowerShell Gallery 的访问

The PowerShell Gallery (www.powershellgallery.com) 是查找新的 PowerShell 命令的理想场所。借助 Install-Module,您可以轻松下载并安装新的 PowerShell 模块。

但是,有时候会失败,有两个主要原因。

有时,Windows 10 自带的 PowerShellGet 模块已过时。然后,您会收到有关提示缺少参数或错误参数的异常信息。

要解决此问题,您需要手动更新 PowerShellGet。使用管理员权限运行以下行:

1
Install-Module -Name PowerShellGet -Repository PSGallery -Force

第二个常见原因:Windows 版本不支持 TLS 1.2 协议。在这种情况下,您会遇到连接问题。尝试运行以下命令:

1
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

此设置适用于当前的 PowerShell 会话。如果要保留此设置,请将该行放入您的配置文件脚本中。该路径可以在 $profile 中找到。