PowerShell 技能连载 - 研究 PowerShell 命令结果

HTML 是一种简单的格式化输出报告的方法。在这个三部曲系列中,我们首先演示如何生成 HTML 报告,然后展示一种简单的方法将 HTML 报告转为 PDF 文档。

一个简单的研究命令返回结果的方法是使用 Select-Object 显示第一个(随机的)返回结果的所有属性。以下是一个例子:

1
Get-Service | Select-Object -Property * -First 1

通过这种方法,您可以获得一个返回结果,它的所有属性都可见,并且您可以看见这些属性中的实际数值来更好地评估要使用哪些属性。

另一个研究的方法是使用 Get-Member 从更偏技术/定义的角度查看所有可用的属性:

···powershell
Get-Service | Get-Member -MemberType *property


现在,您可以查看所有返回的数据类型,以及每个定义为 "`{get;}`"(只读)或 "`{get;set;}`"(读写)的属性。
<!--本文国际来源:[Investigating PowerShell Command Results](https://blog.idera.com/database-tools/powershell/powertips/investigating-powershell-command-results/)-->

PowerShell 技能连载 - 通过 SNMP 查询高级的打印机

许多网络打印机支持使用 SNMP 查询设备信息,例如序列号、状态和纸仓中纸张的大小,以及错误信息。

在 Windows 系统中,操作系统通过 PowerShell 已经提供所有 SNMP 查询所需的组件。您所需要知道的只是打印机的 IP 地址。当然,请确保它已开机并支持 SNMP。

以下是测试脚本:

1
2
3
4
5
6
7
8
9
10
11
12
# define your printer network IP here:
$Printer_IP = '192.168.2.200'

# connect to printer:
$SNMP = New-Object -ComObject olePrn.OleSNMP
$SNMP.Open($Printer_IP,'public')

# get device description
$SNMP.Get(".1.3.6.1.2.1.25.3.2.1.3.1")
# get device serial number
$SNMP.Get(".1.3.6.1.2.1.43.5.1.1.17.1")
$SNMP.Close()

这段代码非常短小。

更有挑战性的是:除了设备描述和序列号之外您能找到哪些信息?如何知道您可以查询的剩余信息片段的 ID 号。下面是一个最常用的 ID 列表。不过,并非所有打印机都支持所有的 ID:

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
#region list of IDs that you can ask your printer:
# (not all IDs will work with all printers)
$OID_RAW_DATA = ".1.3.6.1.2.1.43.18.1.1"
$OID_CONSOLE_DATA = ".1.3.6.1.2.1.43.16"
$OID_CONTACT = ".1.3.6.1.2.1.1.4.0"
$OID_LOCATION = ".1.3.6.1.2.1.1.6.0"
$OID_SERIAL_NUMBER = ".1.3.6.1.2.1.43.5.1.1.17.1"
$OID_SYSTEM_DESCRIPTION = ".1.3.6.1.2.1.1.1.0"
$OID_DEVICE_DESCRIPTION = ".1.3.6.1.2.1.25.3.2.1.3.1"
$OID_DEVICE_STATE = ".1.3.6.1.2.1.25.3.2.1.5.1"
$OID_DEVICE_ERRORS = ".1.3.6.1.2.1.25.3.2.1.6.1"
$OID_UPTIME = ".1.3.6.1.2.1.1.3.0"
$OID_MEMORY_SIZE = ".1.3.6.1.2.1.25.2.2.0"
$OID_PAGE_COUNT = ".1.3.6.1.2.1.43.10.2.1.4.1.1"
$OID_HARDWARE_ADDRESS = ".1.3.6.1.2.1.2.2.1.6.1"
$OID_TRAY_1_NAME = ".1.3.6.1.2.1.43.8.2.1.13.1.1"
$OID_TRAY_1_CAPACITY = ".1.3.6.1.2.1.43.8.2.1.9.1.1"
$OID_TRAY_1_LEVEL = ".1.3.6.1.2.1.43.8.2.1.10.1.1"
$OID_TRAY_2_NAME = ".1.3.6.1.2.1.43.8.2.1.13.1.2"
$OID_TRAY_2_CAPACITY = ".1.3.6.1.2.1.43.8.2.1.9.1.2"
$OID_TRAY_2_LEVEL = ".1.3.6.1.2.1.43.8.2.1.10.1.2"
$OID_TRAY_3_NAME = ".1.3.6.1.2.1.43.8.2.1.13.1.3"
$OID_TRAY_3_CAPACITY = ".1.3.6.1.2.1.43.8.2.1.9.1.3"
$OID_TRAY_3_LEVEL = ".1.3.6.1.2.1.43.8.2.1.10.1.3"
$OID_TRAY_4_NAME = ".1.3.6.1.2.1.43.8.2.1.13.1.4"
$OID_TRAY_4_CAPACITY = ".1.3.6.1.2.1.43.8.2.1.9.1.4"
$OID_TRAY_4_LEVEL = ".1.3.6.1.2.1.43.8.2.1.10.1.4"
$OID_BLACK_TONER_CARTRIDGE_NAME = ".1.3.6.1.2.1.43.11.1.1.6.1.1"
$OID_BLACK_TONER_CARTRIDGE_CAPACITY = ".1.3.6.1.2.1.43.11.1.1.8.1.1"
$OID_BLACK_TONER_CARTRIDGE_LEVEL = ".1.3.6.1.2.1.43.11.1.1.9.1.1"
$OID_CYAN_TONER_CARTRIDGE_NAME = ".1.3.6.1.2.1.43.11.1.1.6.1.2"
$OID_CYAN_TONER_CARTRIDGE_CAPACITY = ".1.3.6.1.2.1.43.11.1.1.8.1.2"
$OID_CYAN_TONER_CARTRIDGE_LEVEL = ".1.3.6.1.2.1.43.11.1.1.9.1.2"
$OID_MAGENTA_TONER_CARTRIDGE_NAME = ".1.3.6.1.2.1.43.11.1.1.6.1.3"
$OID_MAGENTA_TONER_CARTRIDGE_CAPACITY = ".1.3.6.1.2.1.43.11.1.1.8.1.3"
$OID_MAGENTA_TONER_CARTRIDGE_LEVEL = ".1.3.6.1.2.1.43.11.1.1.9.1.3"
$OID_YELLOW_TONER_CARTRIDGE_NAME = ".1.3.6.1.2.1.43.11.1.1.6.1.4"
$OID_YELLOW_TONER_CARTRIDGE_CAPACITY = ".1.3.6.1.2.1.43.11.1.1.8.1.4"
$OID_YELLOW_TONER_CARTRIDGE_LEVEL = ".1.3.6.1.2.1.43.11.1.1.9.1.4"
$OID_WASTE_TONER_BOX_NAME = ".1.3.6.1.2.1.43.11.1.1.6.1.5"
$OID_WASTE_TONER_BOX_CAPACITY = ".1.3.6.1.2.1.43.11.1.1.8.1.5"
$OID_WASTE_TONER_BOX_LEVEL = ".1.3.6.1.2.1.43.11.1.1.9.1.5"
$OID_BELT_UNIT_NAME = ".1.3.6.1.2.1.43.11.1.1.6.1.6"
$OID_BELT_UNIT_CAPACITY = ".1.3.6.1.2.1.43.11.1.1.8.1.6"
$OID_BELT_UNIT_LEVEL = ".1.3.6.1.2.1.43.11.1.1.9.1.6"
$OID_BLACK_DRUM_UNIT_NAME = ".1.3.6.1.2.1.43.11.1.1.6.1.7"
$OID_BLACK_DRUM_UNIT_CAPACITY = ".1.3.6.1.2.1.43.11.1.1.8.1.7"
$OID_BLACK_DRUM_UNIT_LEVEL = ".1.3.6.1.2.1.43.11.1.1.9.1.7"
$OID_CYAN_DRUM_UNIT_NAME = ".1.3.6.1.2.1.43.11.1.1.6.1.8"
$OID_CYAN_DRUM_UNIT_CAPACITY = ".1.3.6.1.2.1.43.11.1.1.8.1.8"
$OID_CYAN_DRUM_UNIT_LEVEL = ".1.3.6.1.2.1.43.11.1.1.9.1.8"
$OID_MAGENTA_DRUM_UNIT_NAME = ".1.3.6.1.2.1.43.11.1.1.6.1.9"
$OID_MAGENTA_DRUM_UNIT_CAPACITY = ".1.3.6.1.2.1.43.11.1.1.8.1.9"
$OID_MAGENTA_DRUM_UNIT_LEVEL = ".1.3.6.1.2.1.43.11.1.1.9.1.9"
$OID_YELLOW_DRUM_UNIT_NAME = ".1.3.6.1.2.1.43.11.1.1.6.1.10"
$OID_YELLOW_DRUM_UNIT_CAPACITY = ".1.3.6.1.2.1.43.11.1.1.8.1.10"
$OID_YELLOW_DRUM_UNIT_LEVEL = ".1.3.6.1.2.1.43.11.1.1.9.1.10"
#endregion

PowerShell 技能连载 - 订阅锁定和解锁事件

当一个用户在 Windows 系统中锁定了会话,会发出一个事件。当解锁会话的时候会发出另一个事件。两个事件都可以触发 PowerShell 代码,这样可以实现锁定和解锁系统时运行任意 PowerShell 代码。

以下两个函数演示该功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Start-Fun {
$null = Register-ObjectEvent -InputObject ([Microsoft.Win32.SystemEvents]) -EventName "SessionSwitch" -Action {
Add-Type -AssemblyName System.Speech

$synthesizer = New-Object -TypeName System.Speech.Synthesis.SpeechSynthesizer

switch($event.SourceEventArgs.Reason) {
'SessionLock' { $synthesizer.Speak("Bye bye $env:username!") }
'SessionUnlock' { $synthesizer.Speak("Nice to see you again $env:username!") }
}
}
}

function End-Fun {
$events = Get-EventSubscriber | Where-Object { $_.SourceObject -eq [Microsoft.Win32.SystemEvents] }
$jobs = $events | Select-Object -ExpandProperty Action
$events | Unregister-Event
$jobs | Remove-Job
}

运行以上代码,然后运行 Start-Fun 将代码附加到事件上。当您锁定或解锁电脑时,您将会得到一个 PowerShell 发出的语音提示。当然,您可以做其它事情,例如将设备设为节能模式。

运行 End-Fun 来移除事件订阅。

PowerShell 技能连载 - Custom Action for Unknown Commands

每当输入一个无法被 PowerShell 搜索到的命令名时,它都可以通过您定义的自定义操作来扩展命令搜索。

以下是一个快速有趣的示例,演示这个概念:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ExecutionContext.InvokeCommand.CommandNotFoundAction =
{
# second argument is the command that was missing:
$p = $args[1]
# do not try and find it elsewhere
$p.StopSearch = $true

$command = $p.CommandName

# output audio message (make sure your audio is turned up)
$sapi = New-Object -ComObject Sapi.SpVoice
$sapi.Speak("Command $command not found.")
}

当运行完以上代码,然后再次运行一个肯定不存在的命令时,您将会听到一个语音提示(假设您的音量是开启的且扬声器已打开)。当 PowerShell 无法找到一个命令,它会查找所有赋值给 CommandNotFoundAction 的脚本块并执行它。

这个点子是用于改进命令发现。例如,您可能会花时间整理一个流行命令列表和发布这些命令的模块名称。然后,您的自定义脚本块会尝试并查找列表中缺失的命令,并让用户知道缺失的模块名——或者甚至自动下载并安装该模块。

不幸的是,自从 PowerShell 提供了该功能之后,社区中并没有人实现了该功能。现在您可能会有兴趣发明一些比上面发出语音更复杂的功能。

PowerShell 技能连载 - 管理文件共享

Windows 操作系统自带了 “Storage” PowerShell 模块,它可以同时用于 Windows PowerShell 和 PowerShell 7。

这个模块可以管理许多东西,其中之一是文件共享,不过需要管理员特权来运行以下命令。

要获取文件共享的清单(可以通过网络访问的本地文件夹),请试着执行以下代码:

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
PS C:\> Get-FileShare

Name HealthStatus OperationalStatus
---- ------------ -----------------
ADMIN$ Healthy Online
C$ Healthy Online
print$ Healthy Online



PS C:\> Get-FileShare -Name c$

Name HealthStatus OperationalStatus
---- ------------ -----------------
C$ Healthy Online


PS C:\> Get-FileShare -Name c$ | Select-Object -Property *


HealthStatus : Healthy
OperationalStatus : Online
ShareState : Online
FileSharingProtocol : SMB
ObjectId : {1}\\DELL7390\root/Microsoft/Windows/Storage/Providers_v2\WSP_FileShare.ObjectId="{c0c2f698-c81d-11e9-9f6f-80
6e6f6e6963}:FX:SMB||*||C$"
PassThroughClass :
PassThroughIds :
PassThroughNamespace :
PassThroughServer :
UniqueId : smb|DELL7390/C$
ContinuouslyAvailable : False
Description : Standardfreigabe
EncryptData : False
Name : C$
VolumeRelativePath : \
PSComputerName :
CimClass : ROOT/Microsoft/Windows/Storage:MSFT_FileShare
CimInstanceProperties : {ObjectId, PassThroughClass, PassThroughIds, PassThroughNamespace...}
CimSystemProperties : Microsoft.Management.Infrastructure.CimSystemProperties

类似地,其它动词可以执行相关的任务,例如改变一个已有的共享 (Set) 或者创建一个新的共享 (New):

1
2
3
4
5
6
7
8
9
PS C:\> Get-Command -Noun FileShare

CommandType Name Version Source
----------- ---- ------- ------
Function Debug-FileShare 2.0.0.0 Storage
Function Get-FileShare 2.0.0.0 Storage
Function New-FileShare 2.0.0.0 Storage
Function Remove-FileShare 2.0.0.0 Storage
Function Set-FileShare 2.0.0.0 Storage

PowerShell 技能连载 - 在任务栏按钮显示警告状态

当您的脚本需要注意,例如需要用户输入时,我们可以将 Windows 任务栏中的按钮变为橙色,这样用户可以立即知道需要检查您的脚本。

您所需的只是这个模块:

1
Install-Module -Name PsoProgressButton -Scope CurrentUser

下一步,设置一个进度值并且将它设置成红色:

1
2
Set-PsoButtonProgressState -ProgressState Paused
Set-PsoButtonProgressValue -CurrentValue 100

要关闭指示,请运行以下脚本:

1
PS> Set-PsoButtonProgressState -ProgressState NoProgress

PowerShell 技能连载 - 在任务栏按钮显示错误状态

当您的脚本执行时发生错误,如果能通过任务栏按钮显示错误状态,那么是再好不过的了。如果一个任务栏按钮显示红色,您可以立即知道该关注您的脚本。

您所需的只是这个模块:

1
Install-Module -Name PsoProgressButton -Scope CurrentUser

然后,设置一个进度值并且将它设置为红色:

1
2
Set-PsoButtonProgressState -ProgressState Error
Set-PsoButtonProgressValue -CurrentValue 100

要关闭指示,请运行以下代码:

1
PS> Set-PsoButtonProgressState -ProgressState NoProgress

PowerShell 技能连载 - 在任务栏按钮中显示不确定的进度

有时,您不知道脚本的确切进度,但您仍然想通知用户您的脚本“忙”。如果您在 Windows 上运行 PowerShell 脚本,则可以使用任务栏按钮显示不确定的进度条。该进度条将“永远”运行,直到您将其关闭。

您需要的只是安装此模块:

1
Install-Module -Name PsoProgressButton -Scope CurrentUser

接下来,您可以运行下面的命令以打开不确定的进度条。它在代表您的运行 PowerShell 脚本的任务栏按钮内显示:

1
PS> Set-PsoButtonProgressState -ProgressState Indeterminate

要关闭该指示器,请运行以下操作:

1
PS> Set-PsoButtonProgressState -ProgressState NoProgress

PowerShell 技能连载 - Showing Progress in Taskbar Buttons

如果您在 Windows 上运行 PowerShell 脚本,则可以将任务栏按钮用作进度指示器。您需要的只是安装此模块:

1
Install-Module -Name PsoProgressButton -Scope CurrentUser

接下来,您可以运行下面的命令以将任务栏按钮内的进度栏设置为 0 到 100 之间的值。这段代码将指示器设置为 50%:

1
PS> Set-PsoButtonProgressValue -CurrentValue 50

要关闭指示器,请运行以下代码:

1
PS> Set-PsoButtonProgressState -ProgressState NoProgress

PowerShell 技能连载 - 检测多语言在线文档(第 2 部分)

要如何检测一份在线文档支持哪些语言?

如果 URL 使用语言 ID,则很容易创建包含所有可用语言 ID 的 URL 列表。这就是我们到目前为止第一部分中所做的:

1
$list  =  RL  -f

在第二部分中,我们现在确定列表中哪些 URL 实际中可用。但是,只是通过 Invoke-Webrequest 访问该 URL 是不够的:

1
$list  =  RL  -f

事实证明,所有 URL 都能正常地访问 Microsoft WEB 服务器,并返回状态 “OK”(包括不存在的文档地址):

1
PS> New-SCode

这是因为 Microsoft WEB 服务器(与许多其它的一样)首先接受所有 URL。然后,在内部,WEB 服务器弄清楚下一步该怎么做,并将新的 URL 返回到浏览器中。可能返回的是原始的 URL(如果 WEB 服务器找到了资源),也可能是一个全新的URL,例如通用搜索站点或自定义的“未找到”通知。状态 “OK” 与 URL 的有效性并没有关联。

您实际上可以通过禁止自动重定向来查看内部工作过程。对 Invoke-WebRequest 命令添加参数 "-MaximumRedirection 0 -ErrorAction Ignore"

1
$list  =  RL  -f

现在,您看到 Web 服务器如何告诉浏览器,URL 跳转至其他地方,有效地将浏览器重定向到新的 URL。

检查 URL 是否存在,取决于特定的 Web 服务器的工作原理。在微软的例子中,事实证明有效的 URL 会导致单次重定向,而无效的 URL 会导致多次重定向。用重定向的次次数是否为一次,可以区分合法和非法的 URL。

这是最终解决方案,它还支持实时进度条。

它在网格视图窗口中显示可用的本地化在线文档,您可以选择一个或多个以在浏览器中显示。您也可以以 $result 的形式获取结果,然后将其打印到 PDF 并将其提交给其它语言的员工。

1
$list  =  $h.Keys  |  ForEach-Object { $URL  -f