验证 PowerShell 中的函数已成功 运行
Verify a function in PowerShell has run succesfully
我正在编写一个脚本来将现有的 Bit Locker 密钥备份到 Azure AD 中的关联设备,我创建了一个函数来遍历启用了 Bit Locker 的卷并将密钥备份到 Azure,但是我想知道如何检查函数是否已成功完成而没有任何错误。这是我的代码。我已经在函数中添加了一个 try 和 catch 以捕获函数本身的任何错误,但是我如何检查函数是否已成功完成 - 目前我有一个 IF 语句检查最后一个命令是否有 运行 "$ ? - 这是正确的还是我该如何验证?
function Invoke-BackupBDEKeys {
##Get all current Bit Locker volumes - this will ensure keys are backed up for devices which may have additional data drives
$BitLockerVolumes = Get-BitLockerVolume | select-object MountPoint
foreach ($BDEMountPoint in $BitLockerVolumes.mountpoint) {
try {
#Get key protectors for each of the BDE mount points on the device
$BDEKeyProtector = Get-BitLockerVolume -MountPoint $BDEMountPoint | select-object -ExpandProperty keyprotector
#Get the Recovery Password protector - this will be what is backed up to AAD and used to recover access to the drive if needed
$KeyId = $BDEKeyProtector | Where-Object {$_.KeyProtectorType -eq 'RecoveryPassword'}
#Backup the recovery password to the device in AAD
BackupToAAD-BitLockerKeyProtector -MountPoint $BDEMountPoint -KeyProtectorId $KeyId.KeyProtectorId
}
catch {
Write-Host "An error has occured" $Error[0]
}
}
}
#Run function
Invoke-BackupBDEKeys
if ($? -eq $true) {
$ErrorActionPreference = "Continue"
#No errors ocurred running the last command - reg key can be set as keys have been backed up succesfully
$RegKeyPath = 'custom path'
$Name = 'custom name'
New-ItemProperty -Path $RegKeyPath -Name $Name -Value 1 -Force
Exit
}
else {
Write-Host "The backup of BDE keys were not succesful"
#Exit
}
不幸的是,从 PowerShell 7.2.1 开始,automatic $?
variable 在调用 [=82] 后 没有 有意义的值=]written-in-PowerShell function(相对于 binary cmdlet)。 (更直接地说,即使 在 函数中,$?
也只反映 $false
在 catch
块的最开始,正如 Mathias 指出的那样)。
如果 PowerShell 函数具有与二进制 cmdlet 相同的功能,则至少会发出 一个 (非脚本终止)错误,例如 Write-Error
,会将调用者范围内的 $?
设置为 $false
,但目前情况并非如此。
您可以通过使用 advanced function or script, but that is quite cumbersome. The same applies to $PSCmdlet.ThrowTerminatingError()
, which is the only way to create a statement-terminating error from PowerShell code. (By contrast, the throw
语句中的 $PSCmdlet.WriteError()
来解决此限制,该语句会生成 script 终止错误,即终止整个脚本及其调用者 - 除非 try
/ catch
或 trap
语句在调用堆栈的某处捕获错误。
有关详细信息和相关 GitHub 问题的链接,请参阅 this answer。
作为解决方法,我建议:
使您的函数成为 高级 函数,以便启用对 common -ErrorVariable
parameter 的支持 - 它允许您收集所有非终止错误由自选变量中的函数发出。
注意:自选变量名必须传而不用 $
;例如,要收集变量 $errs
,请使用 -ErrorVariable errs
;不要使用 Error
/ $Error
,因为 $Error
is the automatic variable 会收集整个 session.
中发生的所有错误
您可以将其与 common -ErrorAction
parameter 结合使用以在最初消除错误 (-ErrorAction SilentlyContinue
),以便稍后根据需要发出它们。不要使用 -ErrorAction Stop
,因为它会使 -ErrorVariable
变得无用,反而会中止整个脚本。
你可以让错误简单地发生——不需要 try
/ catch
语句:因为你的代码中没有 throw
语句,你的即使在给定的迭代中发生错误,循环也会继续 运行。
- 注意:虽然可以使用
try
/ catch
捕获循环内的终止错误,然后 relay 它们作为 在 catch
块中带有 $_ | Write-Error
的非终止 ,你将在传递给 两次 的变量中得到每个此类错误=26=]。 (如果你没有中继,错误仍然会被收集,但不会打印。)
调用后,检查是否收集到任何错误,以确定是否至少有一个密钥没有备份成功。
顺便说一句:当然,您也可以将函数 output (return) 设为 Boolean ($true
或$false
) 以指示是否发生错误,但这不是设计用于输出 data.
的函数的选项
这是此方法的概要:
function Invoke-BackupBDEKeys {
# Make the function an *advanced* function, to enable
# support for -ErrorVariable (and -ErrorAction)
[CmdletBinding()]
param()
# ...
foreach ($BDEMountPoint in $BitLockerVolumes.mountpoint) {
# ... Statements that may cause errors.
# If you need to short-circuit a loop iteration immediately
# after an error occurred, check each statement's return value; e.g.:
# if (-not $BDEKeyProtector) { continue }
}
}
# Call the function and collect any
# non-terminating errors in variable $errs.
# IMPORTANT: Pass the variable name *without the $*.
Invoke-BackupBDEKeys -ErrorAction SilentlyContinue -ErrorVariable errs
# If $errs is an empty collection, no errors occurred.
if (-not $errs) {
"No errors occurred"
# ...
}
else {
"At least one error occurred during the backup of BDE keys:`n$errs"
# ...
}
这是一个最小的例子,它使用脚本块代替函数:
& {
[CmdletBinding()] param() Get-Item NoSuchFile
} -ErrorVariable errs -ErrorAction SilentlyContinue
"Errors collected:`n$errs"
输出:
Errors collected:
Cannot find path 'C:\Users\jdoe\NoSuchFile' because it does not exist.
如其他地方所述,您正在使用的 try/catch 是阻止错误条件中继的原因。这是设计使然,也是有意使用 try/catch.
的原因
在你的情况下我会做的是创建一个变量或一个文件来捕获错误信息。我向任何名为 'Bob' 的人道歉。这是我经常使用的变量名。
这是一个有效的基本示例:
$bob = (1,2,"blue",4,"notit",7)
$bobout = @{} #create a hashtable for errors
foreach ($tempbob in $bob) {
$tempbob
try {
$tempbob - 2 #this will fail for a string
} catch {
$bobout.Add($tempbob,"not a number") #store a key/value pair (current,msg)
}
}
$bobout #output the errors
这里我们创建了一个数组只是为了使用foreach。把它想象成你的 $BDEMountPoint 变量。
逐一进行,随心所欲。在 }catch{} 中,您只想在失败时说“不是数字”。这是它的输出:
-1
0
2
5
Name Value
---- -----
notit not a number
blue not a number
所有数字都有效(你可以明显抑制输出,这只是为了演示)。
更重要的是,我们在失败时存储了自定义文本。
现在,您可能需要一个信息量更大的错误。您可以像这样获取实际发生的错误:
$bob = (1,2,"blue",4,"notit",7)
$bobout = @{} #create a hashtable for errors
foreach ($tempbob in $bob) {
$tempbob
try {
$tempbob - 2 #this will fail for a string
} catch {
$bobout.Add($tempbob,$PSItem) #store a key/value pair (current,error)
}
}
$bobout
这里我们使用了检查中的当前变量$PSItem,通常也称为$_。
-1
0
2
5
Name Value
---- -----
notit Cannot convert value "notit" to type "System.Int32". Error: "Input string was not in ...
blue Cannot convert value "blue" to type "System.Int32". Error: "Input string was not in a...
您还可以解析实际错误并根据它采取措施或存储自定义消息。但这超出了这个答案的范围。 :)
我正在编写一个脚本来将现有的 Bit Locker 密钥备份到 Azure AD 中的关联设备,我创建了一个函数来遍历启用了 Bit Locker 的卷并将密钥备份到 Azure,但是我想知道如何检查函数是否已成功完成而没有任何错误。这是我的代码。我已经在函数中添加了一个 try 和 catch 以捕获函数本身的任何错误,但是我如何检查函数是否已成功完成 - 目前我有一个 IF 语句检查最后一个命令是否有 运行 "$ ? - 这是正确的还是我该如何验证?
function Invoke-BackupBDEKeys {
##Get all current Bit Locker volumes - this will ensure keys are backed up for devices which may have additional data drives
$BitLockerVolumes = Get-BitLockerVolume | select-object MountPoint
foreach ($BDEMountPoint in $BitLockerVolumes.mountpoint) {
try {
#Get key protectors for each of the BDE mount points on the device
$BDEKeyProtector = Get-BitLockerVolume -MountPoint $BDEMountPoint | select-object -ExpandProperty keyprotector
#Get the Recovery Password protector - this will be what is backed up to AAD and used to recover access to the drive if needed
$KeyId = $BDEKeyProtector | Where-Object {$_.KeyProtectorType -eq 'RecoveryPassword'}
#Backup the recovery password to the device in AAD
BackupToAAD-BitLockerKeyProtector -MountPoint $BDEMountPoint -KeyProtectorId $KeyId.KeyProtectorId
}
catch {
Write-Host "An error has occured" $Error[0]
}
}
}
#Run function
Invoke-BackupBDEKeys
if ($? -eq $true) {
$ErrorActionPreference = "Continue"
#No errors ocurred running the last command - reg key can be set as keys have been backed up succesfully
$RegKeyPath = 'custom path'
$Name = 'custom name'
New-ItemProperty -Path $RegKeyPath -Name $Name -Value 1 -Force
Exit
}
else {
Write-Host "The backup of BDE keys were not succesful"
#Exit
}
不幸的是,从 PowerShell 7.2.1 开始,automatic
$?
variable 在调用 [=82] 后 没有 有意义的值=]written-in-PowerShell function(相对于 binary cmdlet)。 (更直接地说,即使 在 函数中,$?
也只反映$false
在catch
块的最开始,正如 Mathias 指出的那样)。如果 PowerShell 函数具有与二进制 cmdlet 相同的功能,则至少会发出 一个 (非脚本终止)错误,例如
Write-Error
,会将调用者范围内的$?
设置为$false
,但目前情况并非如此。您可以通过使用 advanced function or script, but that is quite cumbersome. The same applies to
$PSCmdlet.ThrowTerminatingError()
, which is the only way to create a statement-terminating error from PowerShell code. (By contrast, thethrow
语句中的$PSCmdlet.WriteError()
来解决此限制,该语句会生成 script 终止错误,即终止整个脚本及其调用者 - 除非try
/catch
或trap
语句在调用堆栈的某处捕获错误。有关详细信息和相关 GitHub 问题的链接,请参阅 this answer。
作为解决方法,我建议:
使您的函数成为 高级 函数,以便启用对 common
-ErrorVariable
parameter 的支持 - 它允许您收集所有非终止错误由自选变量中的函数发出。注意:自选变量名必须传而不用
中发生的所有错误$
;例如,要收集变量$errs
,请使用-ErrorVariable errs
;不要使用Error
/$Error
,因为$Error
is the automatic variable 会收集整个 session.您可以将其与 common
-ErrorAction
parameter 结合使用以在最初消除错误 (-ErrorAction SilentlyContinue
),以便稍后根据需要发出它们。不要使用-ErrorAction Stop
,因为它会使-ErrorVariable
变得无用,反而会中止整个脚本。
你可以让错误简单地发生——不需要
try
/catch
语句:因为你的代码中没有throw
语句,你的即使在给定的迭代中发生错误,循环也会继续 运行。- 注意:虽然可以使用
try
/catch
捕获循环内的终止错误,然后 relay 它们作为 在catch
块中带有$_ | Write-Error
的非终止 ,你将在传递给 两次 的变量中得到每个此类错误=26=]。 (如果你没有中继,错误仍然会被收集,但不会打印。)
- 注意:虽然可以使用
调用后,检查是否收集到任何错误,以确定是否至少有一个密钥没有备份成功。
顺便说一句:当然,您也可以将函数 output (return) 设为 Boolean (
的函数的选项$true
或$false
) 以指示是否发生错误,但这不是设计用于输出 data.
这是此方法的概要:
function Invoke-BackupBDEKeys {
# Make the function an *advanced* function, to enable
# support for -ErrorVariable (and -ErrorAction)
[CmdletBinding()]
param()
# ...
foreach ($BDEMountPoint in $BitLockerVolumes.mountpoint) {
# ... Statements that may cause errors.
# If you need to short-circuit a loop iteration immediately
# after an error occurred, check each statement's return value; e.g.:
# if (-not $BDEKeyProtector) { continue }
}
}
# Call the function and collect any
# non-terminating errors in variable $errs.
# IMPORTANT: Pass the variable name *without the $*.
Invoke-BackupBDEKeys -ErrorAction SilentlyContinue -ErrorVariable errs
# If $errs is an empty collection, no errors occurred.
if (-not $errs) {
"No errors occurred"
# ...
}
else {
"At least one error occurred during the backup of BDE keys:`n$errs"
# ...
}
这是一个最小的例子,它使用脚本块代替函数:
& {
[CmdletBinding()] param() Get-Item NoSuchFile
} -ErrorVariable errs -ErrorAction SilentlyContinue
"Errors collected:`n$errs"
输出:
Errors collected:
Cannot find path 'C:\Users\jdoe\NoSuchFile' because it does not exist.
如其他地方所述,您正在使用的 try/catch 是阻止错误条件中继的原因。这是设计使然,也是有意使用 try/catch.
的原因在你的情况下我会做的是创建一个变量或一个文件来捕获错误信息。我向任何名为 'Bob' 的人道歉。这是我经常使用的变量名。
这是一个有效的基本示例:
$bob = (1,2,"blue",4,"notit",7)
$bobout = @{} #create a hashtable for errors
foreach ($tempbob in $bob) {
$tempbob
try {
$tempbob - 2 #this will fail for a string
} catch {
$bobout.Add($tempbob,"not a number") #store a key/value pair (current,msg)
}
}
$bobout #output the errors
这里我们创建了一个数组只是为了使用foreach。把它想象成你的 $BDEMountPoint 变量。
逐一进行,随心所欲。在 }catch{} 中,您只想在失败时说“不是数字”。这是它的输出:
-1
0
2
5
Name Value
---- -----
notit not a number
blue not a number
所有数字都有效(你可以明显抑制输出,这只是为了演示)。 更重要的是,我们在失败时存储了自定义文本。
现在,您可能需要一个信息量更大的错误。您可以像这样获取实际发生的错误:
$bob = (1,2,"blue",4,"notit",7)
$bobout = @{} #create a hashtable for errors
foreach ($tempbob in $bob) {
$tempbob
try {
$tempbob - 2 #this will fail for a string
} catch {
$bobout.Add($tempbob,$PSItem) #store a key/value pair (current,error)
}
}
$bobout
这里我们使用了检查中的当前变量$PSItem,通常也称为$_。
-1
0
2
5
Name Value
---- -----
notit Cannot convert value "notit" to type "System.Int32". Error: "Input string was not in ...
blue Cannot convert value "blue" to type "System.Int32". Error: "Input string was not in a...
您还可以解析实际错误并根据它采取措施或存储自定义消息。但这超出了这个答案的范围。 :)