PowerShell 技能连载 - 在 try/catch 中使用 ConvertFrom-ErrorRecord

在前一个技能中我们创建了一个名为 ConvertFrom-ErrorRecord 的函数,它能方便地从 PowerShell 的 ErrorRecord 对象中方便地获取错误信息。

您也可以在 catch 块中使用这个函数。只需要先运行以下函数即可:

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
function ConvertFrom-ErrorRecord
{
[CmdletBinding(DefaultParameterSetName="ErrorRecord")]
param
(
[Management.Automation.ErrorRecord]
[Parameter(Mandatory,ValueFromPipeline,ParameterSetName="ErrorRecord", Position=0)]
$Record,

[Object]
[Parameter(Mandatory,ValueFromPipeline,ParameterSetName="Unknown", Position=0)]
$Alien
)

process
{
if ($PSCmdlet.ParameterSetName -eq 'ErrorRecord')
{
[PSCustomObject]@{
Exception = $Record.Exception.Message
Reason = $Record.CategoryInfo.Reason
Target = $Record.CategoryInfo.TargetName
Script = $Record.InvocationInfo.ScriptName
Line = $Record.InvocationInfo.ScriptLineNumber
Column = $Record.InvocationInfo.OffsetInLine
}
}
else
{
Write-Warning "$Alien"
}
}
}

以下是如何在 catch 代码块中使用 ConvertFrom-ErrorRecord 的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
try
{
# this raises an error
Get-Service -Name NonExisting -ErrorAction Stop
}
catch
{
# pipe the errorrecord object through the new function
# to retrieve all relevant error information
# which you then could use to do error logging, or output
# custom error messages
$_ | ConvertFrom-ErrorRecord

}

PowerShell 技能连载 - 转换错误记录

当 PowerShell 抛出错误时,会向 $error 写入一条错误记录,它是一个存储最后发生的错误的数组。

您可以尝试从 ErrorRecord 对象手工解出相关的错误信息,或者使用以下函数:

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
function ConvertFrom-ErrorRecord
{
[CmdletBinding(DefaultParameterSetName="ErrorRecord")]
param
(
[Management.Automation.ErrorRecord]
[Parameter(Mandatory,ValueFromPipeline,ParameterSetName="ErrorRecord", Position=0)]
$Record,

[Object]
[Parameter(Mandatory,ValueFromPipeline,ParameterSetName="Unknown", Position=0)]
$Alien
)

process
{
if ($PSCmdlet.ParameterSetName -eq 'ErrorRecord')
{
[PSCustomObject]@{
Exception = $Record.Exception.Message
Reason = $Record.CategoryInfo.Reason
Target = $Record.CategoryInfo.TargetName
Script = $Record.InvocationInfo.ScriptName
Line = $Record.InvocationInfo.ScriptLineNumber
Column = $Record.InvocationInfo.OffsetInLine
}
}
else
{
Write-Warning "$Alien"
}
}
}

这个函数有两个参数集,一个合法的 ErrorRecord 对象自动绑定到 $Record 参数。如果遇到一个不同类型的,无法被这个函数处理的对象,那么它将绑定到 $Alien

要查看详细的错误信息,请试试这行代码:

1
PS> $error | ConvertFrom-ErrorRecord | Out-GridView

PowerShell 技能连载 - 从 Internet 下载信息(第 8 部分)

在之前的技能中我们演示如何用 Invoke-WebRequest 从 internet 下载文件。然而,它只适用于 HTTP 地址。如果您用的是 HTTPS 地址,将会失败:

1
2
3
4
5
$url = "https://github.com/PowerShellConferenceEU/2018/raw/master/Agenda_psconfeu_2018.pdf"
$destination = "$home\agenda.pdf"

Invoke-WebRequest -Uri $url -OutFile $destination -UseBasicParsing
Invoke-Item -Path $destination

一个简单的解决方法是先运行这行代码:

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

执行完这行代码之后,之前的代码就运行正常了,可以下载 psconf.eu PDF 格式的议程表到您的桌面上,然后尝试打开该文件。

PowerShell 技能连载 - 从 Internet 下载信息(第 7 部分)

在这个迷你系列中的该部分,我们将演示如何用 Invoke-WebRequest 从 internet 下载文件。只需要用 -OutFile 参数。这段代码下载 PNG 格式的 PowerShell 图标到桌面上:

1
2
3
4
5
$url = "http://www.dotnet-lexikon.de/grafik/Lexikon/Windowspowershell.png"
$destination = "$home\powershell.png"

Invoke-WebRequest -Uri $url -OutFile $destination -UseBasicParsing
Invoke-Item -Path $destination

请注意 Invoke-WebRequest 只能从 HTTP 地址下载文件。如果从 HTTPS 地址下载将会报错。请查看我们的下一个技能如何解决它!

PowerShell 技能连载 - 从 Internet 下载信息(第 6 部分)

在之前的技能中我们介绍了如何用 Invoke-WebRequestInvoke-RestMethod 从网页获取 XML 数据。对于 XML 数据,还有一种用 XML 对象自身内建方法的处理方法

这是 Invoke-RestMethod 的使用方法:

1
2
$url = 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'
(Invoke-RestMethod -Uri $url -UseBasicParsing).Envelope.Cube.Cube.Cube

作为另一种选择,请试试这种方法:

1
2
3
4
$url = 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'
$xml = New-Object -TypeName XML
$xml.Load($url)
$xml.Envelope.Cube.Cube.Cube

这种方法快得多。不过它没有提供 Invoke-RestMethod 中的多个参数来处理代理服务器和凭据。

PowerShell 技能连载 - 从 Internet 下载信息(第 5 部分)

在前一个技能中我们演示了如何使用 Invoke-WebRequest 从网页下载 JSON 或 XML 数据。这个例子从 psconf.eu 下载 JSON 格式的议程表:

1
$page = Invoke-WebRequest -Uri powershell.beer -UseBasicParsing $($page.Content | ConvertFrom-Json) | Out-GridView

这个例子下载 XML 格式的货币兑换率:

1
$url = 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'$result = Invoke-WebRequest -Uri $url -UseBasicParsing$xml = [xml]$result.Content$xml.Envelope.Cube.Cube.Cube

现在,有另一个名为 Invoke-RestMethod 的 cmdlet,专门设计来获取对象数据。基本上,它的工作方式和 Invoke-WebRequest 很接近,但是能够自动识别数据格式,并相应地转换它的类型。以下是用一行代码获取 psconf.eu 议程表的例子:

1
$(Invoke-RestMethod -Uri powershell.beer -UseBasicParsing) | Out-GridView

这是轻松地获取货币兑换率的方法:

1
2
$url = 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'
(Invoke-RestMethod -Uri $url -UseBasicParsing).Envelope.Cube.Cube.Cube

PowerShell 技能连载 - 从 Internet 下载信息(第 4 部分)

在前一个技能中我们介绍了如何使用 Invoke-WebRequest 从网页下载数据,例如从一个提供随机借口的网页中获取借口。然而,当您做测试的时候,有可能每次都获取到相同的借口(或数据)。

1
2
3
4
5
6
7
8
9
10
$url = 'http://pages.cs.wisc.edu/~ballard/bofh/bofhserver.pl'
$page = Invoke-WebRequest -Uri $url -UseBasicParsing
$content = $page.Content

$pattern = '(?s)<br><font size\s?=\s?"\+2">(.+)</font'

if ($page.Content -match $pattern)
{
$matches[1]
}

最有可能的原因是处在一个代理服务器之后,代理服务器缓存了网站信息。要解决这个问题,只需要将 URL 加上一个类似这样的随机参数:

1
2
3
4
5
6
7
8
9
10
$url = "http://pages.cs.wisc.edu/~ballard/bofh/bofhserver.pl?$(Get-Random)"
$page = Invoke-WebRequest -Uri $url -UseBasicParsing
$content = $page.Content

$pattern = '(?s)<br><font size\s?=\s?"\+2">(.+)</font'

if ($page.Content -match $pattern)
{
$matches[1]
}

PowerShell 技能连载 - 从 Internet 下载信息(第 3 部分)

在前一个技能中,我们演示了如何使用 Invoke-WebRequest 从网页下载数据,并且处理 JSON 或 XML 格式的数据。然而,多数网页包含的是纯 HTML 数据。您可以使用正则表达式来从纯 HTML 中提取信息。

以下是获得网页内容的方法:

1
2
3
$url = 'http://pages.cs.wisc.edu/~ballard/bofh/bofhserver.pl'
$page = Invoke-WebRequest -Uri $url -UseBasicParsing
$page.Content

这个例子中的网页提供随机的借口。要获得最终的借口,得先创建正则表达式模式:

1
2
3
4
5
6
7
8
9
10
$url = 'http://pages.cs.wisc.edu/~ballard/bofh/bofhserver.pl'
$page = Invoke-WebRequest -Uri $url -UseBasicParsing
$content = $page.Content

$pattern = '(?s)<br><font size\s?=\s?"\+2">(.+)</font'

if ($page.Content -match $pattern)
{
$matches[1]
}

当您运行这段代码时,它可以提供一个新的借口。我们在此不深入讨论正则表达式。该正则表达式的基本原理是查找 )<br><font size\s?=\s?"\+2"> 这样的静态文本,然后取出之后的任何文本 ((.+)),直到结尾的静态文本 (</font)。$matches[1] 的值就是正则表达式中的第一个模式代表的内容,在这里就是我们要提取的借口。

PowerShell 技能连载 - 从 Internet 下载信息(第 2 部分)

Invoke-WebRequest 可以下载任意类型的信息,而且可以根据您的需要将它转为任意类型。在前一个技能里,我们演示了如何处理 JSON 数据。现在我们来看看返回 XML 数据的网页:

这个例子从欧洲中央银行获取当前的货币兑换率:

1
2
3
$url = 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'
$result = Invoke-WebRequest -Uri $url -UseBasicParsing
$result.Content

以下是结果:

1
2
3
4
$url = 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'
$result = Invoke-WebRequest -Uri $url -UseBasicParsing
$xml = [xml]$result.Content
$xml.Envelope.Cube.Cube.Cube

PowerShell 技能连载 - 从 Internet 下载信息(第 1 部分)

PowerShell 带来了两个 cmdlet,可以用来从 internet 获取信息。今天,我们重点关注 Invoke-WebRequest

这个 cmdlet 提供了一个简单的 web 客户端。传给它一个 URL,它就可以帮您下载该网页。以下简单的几行代码可以帮您下载 psconf.eu 的议程表:

1
2
$page = Invoke-WebRequest -Uri powershell.beer -UseBasicParsing
$page.Content

由于它是 JSON 格式的,所以可以将它通过管道传递给 ConvertFrom-Json 来获得对象:

1
2
$page = Invoke-WebRequest -Uri powershell.beer -UseBasicParsing
$page.Content | ConvertFrom-Json | Out-GridView

然而,有些时候(例如这个例子),它并不能正确地“展开”集合结果,所以得手工操作它:

1
2
$page = Invoke-WebRequest -Uri powershell.beer -UseBasicParsing
$($page.Content | ConvertFrom-Json) | Out-GridView

通过这种方法,您可以下载网页上的任意信息,然后处理它。如果一个 web 页返回的是 XML,您可以将它转换为 XML,而如果它是纯 HTML,您可以使用正则表达式来提取您需要的信息。更多信息请见将来的技能。