PowerShell 技能连载 - 解析原始数据和日志文件(第 1 部分)

大多数原始日志文件以表格形式出现:尽管它们可能不是功能齐全的 CSV 格式,但它们通常具有列和某种分隔符,有时甚至包含标题。

这是从 IIS 日志中获取的示例。当您查看它时时,会发现许多日志文件从根本上以表格方式组织它们的数据,如下所示:

#Software: Microsoft Internet Information Services 10.0
#Version: 1.0
#Date: 2018-02-02 00:03:04
#Fields: date time s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs(User-Agent) cs(Referer) sc-status sc-substatus sc-win32-status time-taken
2021-05-02 00:00:04 10.10.12.5 GET /Content/anonymousCheckFile.txt - 8530 - 10.22.121.248 - - 200 0 0 0
2021-05-02 00:00:04 10.10.12.5 GET /Content/anonymousCheckFile.txt - 8531 - 10.22.121.248 - - 200 0 0 2

与编写复杂的代码来读取和解析日志文件的数据相比,将日志文件与标准 CSV 格式进行比较并查看是否可以这样处理会非常有价值。

在上面的 IIS 日志示例中,结果将是这样:

  • 分隔符是一个空格(不是逗号)
  • 字段(列名)记录在注释行(而不是标题行)中

知道这一点后,请使用 Import-Csv(用于 CSV 的快速内置 PowerShell 解析器)来快速解析日志文件并将其转换为对象。您需要做的就是告诉 Import-Csv 您的日志文件与标准 CSV 格式功能的不同之处:

1
2
3
$Path = "c:\logs\l190202.log"

Import-Csv -Path $Path -Delimiter ' ' -Header date, time, s-ip, cs-method, cs-uri-stem, cs-uri-query, s-port, cs-username, c-ip, csUser-Agent, csReferer, sc-status ,sc-substatus, sc-win32-status, time-taken

在此示例中,使用 -Delimiter 告诉 Import-Csv 分隔符是一个空格,并且由于没有定义标题,请使用 -Header 并粘贴在开头的日志文件注释中找到的标题名称。

如果您不知道日志的标题名称,只需提供一个字符串数组,或使用此参数:

1
Import-Csv -Header (1..50)

这将为日志文件的列分配数字。

PowerShell 技能连载 - 修复 CSV 导出(第 2 部分)

在上一个技能中,我们指出了将对象转换为 CSV 时的一个普遍问题:任何包含数组的属性都将显示数组数据类型而不是数组内容。下面是一个例子:

1
2
3
4
5
6
PS> Get-Service | Select-Object -Property Name, DependentServices, RequiredServices | ConvertTo-Csv

#TYPE Selected.System.ServiceProcess.ServiceController
"Name","DependentServices","RequiredServices"
"AarSvc_e1277","System.ServiceProcess.ServiceController[]","System.ServiceProcess.ServiceController[]"
"AdobeARMservice","System.ServiceProcess.ServiceController[]","System.ServiceProcess.ServiceController[]"

在上一个技能中,我们还展示了该问题的手动解决方案:您总是可以使用 -join 运算符手动将任何数组属性的内容转换为字符串:

1
2
3
4
5
6
7
8
9
10
Get-Service |
Select-Object -Property Name, DependentServices, RequiredServices |

ForEach-Object {
$_.DependentServices = $_.DependentServices -join ','
$_.RequiredServices = $_.RequiredServices -join ','
return $_
} |

ConvertTo-Csv

数据现已“修复”,数组属性内容显示正确:

"Name","DependentServices","RequiredServices"
"AppIDSvc","applockerfltr","RpcSs,CryptSvc,AppID"
"Appinfo","","RpcSs,ProfSvc"
"AppVClient","","AppvVfs,RpcSS,AppvStrm,netprofm"
"AppXSvc","","rpcss,staterepository"
"AssignedAccessManagerSvc","",""
...

但是,将数组属性转换为扁平字符串可能需要大量手动工作,因此这里有一个名为 Convert-ArrayPropertyToString 的新函数,它会自动完成所有转换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Convert-ArrayPropertyToString
{
process
{
$original = $_
Foreach ($prop in $_.PSObject.Properties)
{
if ($Prop.Value -is [Array] -and $prop.MemberType -ne 'AliasProperty')
{
Add-Member -InputObject $original -MemberType NoteProperty -Name $prop.Name -Value ($prop.Value -join ',') -Force
}
}
$original
}
}

要将对象转换为 CSV 而不丢失数组信息,只需将它通过管道传给新函数:

1
2
3
4
5
6
7
8
9
10
PS> Get-Service | Select-Object -Property Name, DependentServices, RequiredServices | Convert-ArrayPropertyToString | ConvertTo-Csv

#TYPE Selected.System.ServiceProcess.ServiceController
"Name","DependentServices","RequiredServices"
"AarSvc_e1277","",""
"AppIDSvc","applockerfltr","RpcSs,CryptSvc,AppID"
"Appinfo","","RpcSs,ProfSvc"
"AppMgmt","",""
"AppReadiness","",""
"AppVClient","","AppvVfs,RpcSS,AppvStrm,netprofm"

厉害吧?新函数能为您完成所有工作,它适用于任何对象:

1
2
3
4
5
6
7
8
9
PS> [PSCustomObject]@{
Name = 'Tobias'
Array = 1,2,3,4
Date = Get-Date
} | Convert-ArrayPropertyToString

Name Date Array
---- ---- -----
Tobias 06.05.2021 11:30:58 1,2,3,4

Convert-ArrayPropertyToString 使用在任何 PowerShell 对象都包含的 PSObject 隐藏属性来获取属性名称。接下来,它检查数组内容的所有属性。如果找到,它会自动将数组转换为逗号分隔的字符串。

为了能够使用新的扁平字符串内容覆盖现有属性——即使属性被写保护——它使用 Add-Member 并使用 -Force 隐藏属性。新的扁平字符串内容并没有真正覆盖属性。相反,它们被添加并优先使用。实际上,任何对象——即使其属性被写保护——都可以调整。

现在,每当您需要创建 Excel 报告或将数据导出到 CSV 时,您都可以轻松保留数组内容。

PowerShell 技能连载 - 修复 CSV 导出(第 1 部分)

当您将数据转换为 CSV 时,您可能会遇到一个很烦恼的情况:某些属性不是显示原始数据。下面是一个例子:

1
2
3
4
5
6
PS> Get-Service | Select-Object -Property Name, DependentServices, RequiredServices | ConvertTo-Csv

#TYPE Selected.System.ServiceProcess.ServiceController
"Name","DependentServices","RequiredServices"
"AarSvc_e1277","System.ServiceProcess.ServiceController[]","System.ServiceProcess.ServiceController[]"
"AdobeARMservice","System.ServiceProcess.ServiceController[]","System.ServiceProcess.ServiceController[]"

如您所见,DependentServicesRequiredServices 属性和所有服务显示的内容是一样的。

当属性包含数组时会发生这种情况。扁平化二维导出格式(例如 CSV)无法显示数组,因此将显示数组数据类型。这对用户来说当然根本没有帮助。

在我们解决它之前先总结一下:您在这里看到的是许多场景中的严重问题。它不仅影响CSV导出,还影响导出为 Excel 或其他二维表格格式。

要解决这个问题,您必须将所有数组转换为字符串。您可以手动或自动执行此操作。在这个技能中,我们首先展示手动方法来关注效果。在未来的技能中,我们会自动执行相同的操作。

这是从上方正确导出所选数据的手动方法:

1
2
3
4
5
6
7
8
9
10
Get-Service |
Select-Object -Property Name, DependentServices, RequiredServices |

ForEach-Object {
$_.DependentServices = $_.DependentServices -join ','
$_.RequiredServices = $_.RequiredServices -join ','
return $_
} |

ConvertTo-Csv

数据现在显示所有数组内容,因为 ForEach-Object 循环已使用 -join 运算符将数组内容转换为逗号分隔的字符串。

"Name","DependentServices","RequiredServices"
"AppIDSvc","applockerfltr","RpcSs,CryptSvc,AppID"
"Appinfo","","RpcSs,ProfSvc"
"AppVClient","","AppvVfs,RpcSS,AppvStrm,netprofm"
"AppXSvc","","rpcss,staterepository"
"AssignedAccessManagerSvc","",""
...

只要您通过 Select-Object 操作原始数据,就可以进行这种“调整”:Select-Object 始终复制(克隆)信息,因此一旦 Select-Object 处理了数据,您就拥有这些对象并可以以任何方式更改其属性。

PowerShell 技能连载 - 导出不带引号的CSV(和其他转换技巧)

PowerShell 附带了一堆 Export-ConvertTo- cmdlet,因此您可以将对象数据序列化为 CSV、JSON、XML和其他格式。很好,但是创建自己的导出功能并不难。

例如,Windows PowerShell 中的 Export-Csv 始终对数值添加双引号。如果你不喜欢带双引号的 CSV,就不好办了。 PowerShell 7 已解决此问题,其中包含额外的参数,但创建自己的 Export-Csv 函数根本并不难。

这是一个简单的例子:

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
function ConvertTo-MyCsv
{
param
(
[char]
$Delimiter = ','
)
begin
{
$init = $false
}
process
{
# write the headers
if ($init -eq $false)
{
$_.PSObject.Properties.Name -join $Delimiter
$init = $true
}

# write the items
$_.PSObject.Properties.Value -join $Delimiter
}
}

$a = Get-Service | ConvertTo-MyCsv

这的确是所有的内容了:ConvertTo-MyCsv 将对象转换为没有双引号的 CSV,如果需要,甚至可以选择分隔符(默认为 “,“)。

要快速检查生成的CSV的完整性,请将其转换回对象:

1
PS> $a | ConvertFrom-CSV | Out-GridView

一切都很好,转换生效了。它不比 ConvertTo-Csv 慢。

显然,如果任何数据值包含分隔符(这就是为什么 ConvertTo-Csv 为它们添加双引号的原因),则转换将失败。但这不是这里的重点。您可以轻松微调函数。更重要的是,PowerShell 中将对象自动转换为文本的艺术。

要将对象转换为 CSV(或任何其他格式),只需访问隐藏的 PSObject 属性(可用于任何 PowerShell 对象)。它描述了对象,并提供所有属性名称,值和数据类型。

PowerShell 技能连载 - 更好的 PowerShell 帮助(第 3 部分)

在上一个技能中,我们使用自定义代理功能对原始的 Get-Help cmdlet 进行了“影子”处理。该功能检查命令是否存在联机帮助,如果存在,则默认情况下会打开丰富的联机帮助。这极大地改善了 PowerShell 中的帮助体验。

但是,通过简单的调整,您可以使该功能更进一步:为什么不同时检查另一种方式呢?如果用户使用 -Online 参数,但是没有在线帮助,则 Get-Help 会抛出一个丑陋的异常。显示内置的本地帮助而不是抛出异常,不是更好吗?

当用户将 Get-Help 的默认值设置为 -Online 时,也可使该函数能适应 $PSDefaultParameterValue 的调整。

这是更新的函数:

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
function Get-Help{
\# clone the original param block taken from Get-Help [CmdletBinding(DefaultParameterSetName='AllUsersView', HelpUri='https://go.microsoft.com/fwlink/?LinkID=113316')]
param(
[Parameter(Position=0, ValueFromPipelineByPropertyName)]
[string]
$Name, [Parameter(ParameterSetName='Online', Mandatory)]
[switch]
$Online, [ValidateSet('Alias','Cmdlet','Provider','General','FAQ','Glossary','HelpFile','ScriptCommand','Function','Filter','ExternalScript','All','DefaultHelp','Workflow','DscResource','Class','Configuration')]
[string[]]
$Category, [string]
$Path, [string[]]
$Component, [string[]]
$Functionality, [string[]]
$Role, [Parameter(ParameterSetName='DetailedView', Mandatory)]
[switch]
$Detailed, [Parameter(ParameterSetName='AllUsersView')]
[switch]
$Full, [Parameter(ParameterSetName='Examples', Mandatory)]
[switch]
$Examples, [Parameter(ParameterSetName='Parameters', Mandatory)]
[string]
$Parameter, [Parameter(ParameterSetName='ShowWindow', Mandatory)]
[switch]
$ShowWindow )

begin {
\# we do the adjustments only when the user has submitted \# the -Name, -Category, and -Online parameters
if ( (@($PSBoundParameters.Keys) -ne 'Name' -ne 'Category' -ne 'Online'). Count -eq 0 )
{
\# check whether there IS online help available at all
\# retrieve the help URI $help = Microsoft.PowerShell.Core\Get-Command -Name $Name
\# reset the parameter -Online based on availability of online help $PSBoundParameters['Online']= [string]::IsNullOrWhiteSpace($help.HelpUri) -eq $false }

\# once the parameter adjustment has been processed, call the original \# Get-Help cmdlet with the parameters found in $PSBoundParameters
\# turn the original Get-Help cmdlet into a proxy command receiving the \# adjusted parameters \# with a proxy command, you can invoke its begin, process, and end \# logic separately. That's required to preserve pipeline functionality $cmd = Get-Command -Name 'Get-Help' -CommandType Cmdlet $proxy = {& $cmd @PSBoundParameters}. GetSteppablePipeline($myInvocation.CommandOrigin)

\# now, call its default begin, process, and end blocks in the appropriate \# script blocks so it integrates in real-time pipelines $proxy.Begin($PSCmdlet)
}

process { $proxy.Process($_) }

end { $proxy.End() }

\# use the original help taken from Get-Help for this function <#
.ForwardHelpTargetName Microsoft.PowerShell.Core\Get-Help .ForwardHelpCategory Cmdlet \#>}

运行这段代码之后,无论使用 Get-Helphelp 还是公共参数 -?,您的帮助系统现在都变得更加智能。

如果有可用于命令的联机帮助,则默认情况下显示:

1
2
3
PS> Get-Help Get-Service

PS> Get-Service -?

如果没有可用的联机帮助,它将始终显示本地帮助(即使您不小心指定了 -Online 或使用 $PSDefaultParameterValue 来显式使用 -Online):

1
2
3
PS> Connect-IscsiTarget -?

PS> Get-Help Connect-IscsiTarget -Online

并且,如果您指定其他参数,它们仍然可以按预期继续工作:

1
PS> Get-Help Get-Service -ShowWindow

本质上,该调整仅包含同时提供丰富在线帮助内容,并在可用时显示它。

如果您喜欢此功能,请将其添加到配置文件脚本中,以便在启动 PowerShell 时对其进行定义。路径可以在这里找到:$profile.CurrentUserAllHosts

PowerShell 技能连载 - 更好的 PowerShell 帮助(第 2 部分)

在上一个技能中,我们更改了 Get-Help 的默认参数值,以在您使用 Get-Help 或通用参数 -? 时自动显示丰富的联机帮助。但是,当 cmdlet 没有联机帮助时,此方法会产生错误。

更好的方法是首先检查给定命令是否存在联机帮助,然后才打开联机帮助。如果没有在线帮助,则应显示默认的本地帮助。

此方法无法通过默认参数实现。相反,Get-Help cmdlet 本身需要进行调整。要将逻辑添加到 Get-Help,可以使用以下代理函数:

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
function Get-Help{
\# clone the original param block taken from Get-Help [CmdletBinding(DefaultParameterSetName='AllUsersView', HelpUri='https://go.microsoft.com/fwlink/?LinkID=113316')]
param(
[Parameter(Position=0, ValueFromPipelineByPropertyName)]
[string]
$Name, [Parameter(ParameterSetName='Online', Mandatory)]
[switch]
$Online, [ValidateSet('Alias','Cmdlet','Provider','General','FAQ','Glossary','HelpFile','ScriptCommand','Function','Filter','ExternalScript','All','DefaultHelp','Workflow','DscResource','Class','Configuration')]
[string[]]
$Category, [string]
$Path, [string[]]
$Component, [string[]]
$Functionality, [string[]]
$Role, [Parameter(ParameterSetName='DetailedView', Mandatory)]
[switch]
$Detailed, [Parameter(ParameterSetName='AllUsersView')]
[switch]
$Full, [Parameter(ParameterSetName='Examples', Mandatory)]
[switch]
$Examples, [Parameter(ParameterSetName='Parameters', Mandatory)]
[string]
$Parameter, [Parameter(ParameterSetName='ShowWindow', Mandatory)]
[switch]
$ShowWindow )

begin {
\# determine whether -Online should be made a default if (
\# user submitted -Name only ( $PSBoundParameters.Count -eq 1 -and
$PSBoundParameters.ContainsKey('Name')
) -or \# system submitted -Name and -Category (when using -?) (
$PSBoundParameters.Count -eq 2 -and
$PSBoundParameters.ContainsKey('Name') -and
$PSBoundParameters.ContainsKey('Category')
)
)
{
\# prerequisites are OK, now check whether there IS online help \# available at all
\# retrieve the help URI $help = Microsoft.PowerShell.Core\Get-Command -Name $Name
\# set the -Online parameter only if there is a help URI $PSBoundParameters['Online']= [string]::IsNullOrWhiteSpace($help.HelpUri) -eq $false }

\# once the parameter adjustment has been processed, call the original \# Get-Help cmdlet with the parameters found in $PSBoundParameters
\# turn the original Get-Help cmdlet into a proxy command receiving the \# adjusted parameters \# with a proxy command, you can invoke its begin, process, and end \# logic separately. That's required to preserve pipeline functionality $cmd = Get-Command -Name 'Get-Help' -CommandType Cmdlet $proxy = {& $cmd @PSBoundParameters}. GetSteppablePipeline($myInvocation.CommandOrigin)

\# now, call its default begin, process, and end blocks in the appropriate \# script blocks so it integrates in real-time pipelines $proxy.Begin($PSCmdlet)
}

process { $proxy.Process($_) }

end { $proxy.End() }

\# use the original help taken from Get-Help for this function <#
.ForwardHelpTargetName Microsoft.PowerShell.Core\Get-Help .ForwardHelpCategory Cmdlet \#>}

运行此代码时,Get-Help 函数现在将覆盖原始的 Get-Help cmdlet。在内部,该函数调用原生的 cmdlet,但在此之前,该函数检查 -Online 是否应设为默认参数。

现在,仅当用户未提交任何有冲突的参数时才发生这种情况,并且仅当首先有可用于所请求命令的联机帮助时才发生这种情况。

现在,无论何时使用 Get-Help 或通用参数 -?,您都会获得丰富的在线帮助(如果可用)或本地默认帮助。

试试以下代码:

1
2
3
4
5
6
7
PS> Get-Service -?

PS> Connect-IscsiTarget -?

PS> Get-Help Get-Service

PS> Get-Help Get-Service -ShowWindow

由于有可用于 Get-Service 的联机帮助,因此第一个调用将打开浏览器窗口并显示帮助。第二个调用说明了没有可用的联机帮助时发生的情况:此处未将 “-Online“ 作为默认参数,而是显示了默认的本地帮助。

第三和第四次调用说明 Get-Help 仍然可以正常运行。默认情况下,该命令现在会打开联机帮助,但是如果您添加其他参数(例如 -ShowWindow),它们仍将按预期运行。

如果您喜欢此功能,则应将其添加到您的配置文件脚本中。

注意:如果您已通过 $PSDefaultParameterGet-Help 设置了任何默认参数(即,按照前面的提示进行操作时),则这些参数将生效,并且上面的功能无法为您带来任何改善。

确保您没有定义任何会影响 “Get-Help“ 的默认参数:

1
PS> $PSDefaultParameterValues.Keys.ToLower() -like 'get-help*'

PowerShell 技能连载 - 更好的 PowerShell 帮助(第 1 部分)

许多 cmdlet 提供了丰富的联机帮助,您可以使用 Get-Help 并加上 -Online 参数来自动打开 cmdlet 的网页:

1
PS> Get-Help -Name Get-Service -Online

每个 cmdlet 还支持公用参数 -?。不过,它仅显示了有限的内置本地帮助:

1
2
3
4
5
6
7
8
9
PS> Get-Service -?

NAME
Get-Service

SYNTAX
Get-Service [-ComputerName <System.String[]>] [-DependentServices] -DisplayName <System.String[]> [-Exclude
<System.String[]>] [-Include <System.String[]>] [-RequiredServices] []
...

但是,您可以通过一个简单的技巧告诉 PowerShell 也对内置参数 -? 使用丰富的联机帮助。请运行这段代码:

1
PS> $PSDefaultParameterValues['Get-Help:Online'] = $true

此命令自动为 Get-Help -Online 参数设置一个新的默认值,并且因为 -? 内部使用 Get-Help,您现在只需添加 -? 到命令名称就可以获得大多数 PowerShell cmdlet 的丰富在线帮助。

1
PS> Get-Service -?

真是太棒了,因此您可能需要将该命令添加到自动启动配置文件脚本中。该脚本的路径可以在 $profile.CurrentUserAllHosts 中找到。它可能尚不存在,所以您可能必须创建它。

但是,有一个警告:如果命令没有在线帮助,那么您现在会收到一条错误消息,提示没有在线帮助。在即将发布的技能中,我们将单独解决此问题。

PowerShell 技能连载 - 检测 Wi-Fi 信号强度(第 3 部分)

在上一个技巧中,我们介绍了免费的 PowerShell 模块 Get-WLANs ,该模块可以访问 Windows Wi-Fi 框架并返回信息,例如信号强度。这是下载并安装它的命令:

1
Install-Module -Name Get-WLANs -Scope CurrentUser -Force

尽管您可以简单地使用其新命令 Get-WLANs(如上一技巧中所述),但该模块还添加了一个新的 .NET 类型,您可以在运行该命令后使用它。首先运行此命令以初始化新的 .NET 类型:

1
2
# "initialize" the new type
$null = Get-WLANs

接下来,创建一个原生的 “WlanClient” 对象:

1
$wc = [NativeWifi.WlanClient]::New()

现在,您可以转储有关系统中所有可用的 Wi-Fi 适配器的所有技术细节:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PS> $wc.Interfaces


Autoconf : True
BssType : Any
InterfaceState : Connected
Channel : 36
RSSI : 29
RadioState : NativeWifi.Wlan+WlanRadioState
CurrentOperationMode : ExtensibleStation
CurrentConnection : NativeWifi.Wlan+WlanConnectionAttributes
NetworkInterface : System.Net.NetworkInformation.SystemNetworkInterface
InterfaceGuid : 7d6c33b7-0354-4ad7-a72f-5a1a5cbb1a9b
InterfaceDescription : Killer(R) Wi-Fi 6 AX1650s 160MHz Wireless Network Adapter (201D2W)
InterfaceName : WLAN

Interfaces“ 属性返回一个数组,因此您的第一个 Wi-Fi 适配器由以下形式表示:

1
$wc.Interfaces[0]

它提供了很多方法,例如,检索可访问的可用 Wi-Fi 网络列表。这将扫描新的网络:

1
2
$wc.Interfaces[0].Scan()
Start-Sleep -Seconds 1

以下代码转储可使用的网络列表(包括信号强度,频率和信道):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
PS> $wc.Interfaces[0].GetAvailableNetworkList(3)


Dot11PhyTypes : {8}
profileName : internetcafe
dot11Ssid : NativeWifi.Wlan+Dot11Ssid
dot11BssType : Infrastructure
numberOfBssids : 3
networkConnectable : True
wlanNotConnectableReason : Success
morePhyTypes : False
wlanSignalQuality : 81
securityEnabled : True
dot11DefaultAuthAlgorithm : RSNA_PSK
dot11DefaultCipherAlgorithm : CCMP
flags : Connected, HasProfile

(...)

此对象模型为提供了丰富的方法和属性来控制和管理 Wi-Fi 网络适配器。例如,此代码转储可用的 SSID 列表及其信号强度:

1
2
3
4
5
6
7
8
9
10
11
$ssid = @{
N='SSID'
E={ [System.Text.Encoding]::Ascii.GetString( $_.dot11ssid.SSID,
0,
$_.dot11ssid.SSIDLength )
}
}

$wc.Interfaces[0].GetAvailableNetworkList(3) |
Select-Object -Property $ssid, wlanSignalQuality, profileName |
Where-Object SSID

结果看起来类似这样:

SSID                       wlanSignalQuality profileName
----                       ----------------- -----------
internetcafe                              81 internetcafe
internetcafe                              87 internetcafe 2
internetcafe                              87
DIRECT-fb-HP M477 LaserJet                31
Guest                                     67

PowerShell 技能连载 - 检测 Wi-Fi 信号强度(第 2 部分)

在上一个技能中,我们使用 netsh.exe 来确定 Wi-Fi 信号强度。由于 netsh.exe 返回的是原始格式的文本,因此需要大量的文本运算符技巧来提取实际的信号强度。通过调用结构化的 API 和对象成员直接访问信息始终是更优的方法。

坦白地说,没有内置的 PowerShell 方法可以以面向对象和结构化的方式访问 Wi-Fi 信息。但是,借助 PowerShell Gallery 中的免费 PowerShell 模块,您可以检索大量有用的 Wi-Fi 信息:

1
Install-Module -Name Get-WLANs -Scope CurrentUser -Force

该模块基本上随附访问任何最新 Windows 操作系统中存在的内置 Windows “Managed Wi-Fi” 框架所需的 C# 代码。

安装该模块后,新命令 Get-WLANs 将返回有关所有可访问的 Wi-Fi 网络的面向对象的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
PS> Get-WLANs

SSID : internetcafe
BSSID : 38:96:ED:0E:31:AD
RSSI : -63
QUALITY : 81
FREQ : 5180
CHANNEL : 36
PHY : VHT
CAPABILITY : 0x1511
IESIZE : 393

SSID : guests
BSSID : 3E:96:ED:0E:31:AD
RSSI : -69
QUALITY : 70
FREQ : 5180
CHANNEL : 36
PHY : VHT
CAPABILITY : 0x1511
IESIZE : 286
(...)

这也包括有关信号强度的信息(在 Quality 属性中)。

PowerShell 技能连载 - 检测 Wi-Fi 信号强度(第 1 部分)

如果您已连接到无线网络,则以下这行代码可提供当前信号强度:

1
2
PS> @(netsh wlan show interfaces) -match '^\s+Signal' -replace '^\s+Signal\s+:\s+',''
80%

信号强度来自 netsh.exe 提供的文本输出。将其包含在 @() 中可确保它始终返回一个数组。然后,-match 运算符使用正则表达式来标识在行首包含单词 “Signal” 的行。后面的 -replace 运算符使用正则表达式删除信号强度之前的文本。

如果您不喜欢正则表达式,则可以使用其他方法来实现,例如:

1
2
PS> (@(netsh wlan show interfaces).Trim() -like 'Signal*').Split(':')[-1].Trim()
80%

这段代码中,将首先修剪 netsh.exe 返回的每一行(删除两端的空白)。接下来,经典的 -like 运算符选择以 “Signal” 开头的行。然后用 “:” 分隔该行,并使用最后一部分(索引为 -1)。再次清除信号强度两端的空白。

如果两个命令均未返回任何内容,请检查您的计算机是否已连接到无线网络。您可能还希望仅使用 netsh.exe 命令的参数来运行它,而没有任何文本运算符来查看命令的原始输出。