Powershell 自定义递归和 BeginInvoke()
Powershell Custom Recursion and BeginInvoke()
我需要一些有关异步 Powershell 调用的帮助,该调用因 (1) 超出最大递归深度或 (2) 无效输入参数而失败。
基本设置如下...
- 在 VSCode
中完成编码和调试
- Powershell 版本为 5.1.19041 - 桌面版
- 连接到 SSAS 服务器的函数
- 第一个函数调用的递归函数将对象转换为 XML
- 创建 Powershell 会话以执行带参数的函数
- BeginInvoke(),等待完成,处理结果
异步调用是否有什么东西扰乱了我的代码,以至于 $NestLevel 和 $MaxDepth 之间的比较无效或者 $MaxDepth 设置为 0?
我已经检查了一百次,但我没有看到 $MaxDepth 被设置为低于 1 的任何值。"if ( $NestLevel -gt $ MaxDepth ) { return }" 应该终止递归但看起来不像是这样。
我是不是漏掉了什么?
代码示例...
function Test-Recursion {
Param (
[Parameter(Mandatory=$false)]
$InputObject,
[Parameter(Mandatory=$false)]
[ValidateRange(1,100)]
[Int16] $MaxDepth = 2
)
[Int16] $_NestLevel_ += 1
if ( $_NestLevel_ -gt $MaxDepth ) { return }
"<item><nest_level>$($_NestLevel_)</nest_level><max_depth>$($MaxDepth)</max_depth></item>"
& $MyInvocation.MyCommand.ScriptBlock -InputObject $InputObject -MaxDepth $MaxDepth
}
function Get-Info {
Param (
[Parameter(Mandatory=$true)]
[String] $HostName,
[Parameter(Mandatory=$true)]
[ScriptBlock] $ConvertTo
)
$some_collection | `
ForEach-Object {
@(
$_as | ForEach-Object {
& $ConvertTo -InputObject $_ -MaxDepth 3
}
) | Sort-Object
}
}
# this code fails with either...
# - excessive recursion depth
# - invalid $MaxDepth value of 0
try {
$_ps = [powershell]::Create()
$null = $_ps.AddScript(${function:Get-Info})
$null = $_ps.AddParameter('HostName', 'some_server_name')
$null = $_ps.AddParameter('ConvertTo', ${function:Test-Recursion})
$_handle = $_ps.BeginInvoke()
while ( -Not $_handle.IsCompleted ) {
Start-Sleep -Seconds 5
}
$_result = $_ps.EndInvoke($_handle)
$_result
} catch {
$_
} finally {
$_ps.Dispose()
}
# this code works as expected
$items = Get-Info -HostName 'some_server_name' -ConvertTo ${function:Test-Recursion}
$items.table_data
我不会详细说明您希望使用这些功能完成什么,只会指出问题所在,您的 powershell
instance 是 运行由于 无限循环 导致的堆栈内存由您的 Test-Recursion
函数导致 不知道何时停止 ,因此添加了 新参数 ([Int16] $NestingLevel
) 将作为参数传递给 每次递归调用:
function Test-Recursion {
Param (
[Parameter(Mandatory = $false)]
$InputObject,
[Parameter(Mandatory = $false)]
[ValidateRange(1,100)]
[Int16] $MaxDepth = 2,
[Parameter(DontShow)]
[Int16] $NestingLevel
)
[Int16] $NestingLevel += 1
if ( $NestingLevel -gt $MaxDepth ) {
return
}
"<item><nest_level>$NestingLevel</nest_level><max_depth>$MaxDepth</max_depth></item>"
Test-Recursion -InputObject $InputObject -MaxDepth $MaxDepth -NestingLevel $NestingLevel
}
Test-Recursion -InputObject hello -MaxDepth 3
还值得注意的是,您的递归函数可以使用 while
循环进行简化:
function Test-Recursion {
Param (
[Parameter(Mandatory = $false)]
$InputObject,
[Parameter(Mandatory = $false)]
[ValidateRange(1,100)]
[Int16] $MaxDepth = 2,
[Parameter(DontShow)]
[Int16] $NestingLevel
)
while($NestingLevel++ -lt $MaxDepth) {
"<item><nest_level>$NestingLevel</nest_level><max_depth>$MaxDepth</max_depth></item>"
}
}
关于您的最新评论:
The root call initializes the variable to 1. Recursive calls would increment the variable and effectively provide a way to check the current nesting level.
不,变量在函数的每次调用中都存在,然后增量值在递归调用后丢失。但是,您可以在 $script:
scope 中初始化变量,然后它将起作用:
[Int16] $script:_NestLevel_ += 1
就个人而言,我不喜欢在我的函数中使用 $script:
作用域变量,我认为有更好的方法来做到这一点。
一个简单的演示方法是修改函数以在每次调用时输出 $_NestLevel_
,as-is,您会看到控制台显示无限量的 1
.
function Test-Recursion {
Param (
[Parameter(Mandatory = $false)]
$InputObject,
[Parameter(Mandatory = $false)]
[ValidateRange(1,100)]
[Int16] $MaxDepth = 2
)
[Int16] $_NestLevel_ += 1
if ( $_NestLevel_ -gt $MaxDepth ) { return }
$_NestLevel_
& $MyInvocation.MyCommand.ScriptBlock -InputObject $InputObject -MaxDepth $MaxDepth
}
我需要一些有关异步 Powershell 调用的帮助,该调用因 (1) 超出最大递归深度或 (2) 无效输入参数而失败。
基本设置如下...
- 在 VSCode 中完成编码和调试
- Powershell 版本为 5.1.19041 - 桌面版
- 连接到 SSAS 服务器的函数
- 第一个函数调用的递归函数将对象转换为 XML
- 创建 Powershell 会话以执行带参数的函数
- BeginInvoke(),等待完成,处理结果
异步调用是否有什么东西扰乱了我的代码,以至于 $NestLevel 和 $MaxDepth 之间的比较无效或者 $MaxDepth 设置为 0?
我已经检查了一百次,但我没有看到 $MaxDepth 被设置为低于 1 的任何值。"if ( $NestLevel -gt $ MaxDepth ) { return }" 应该终止递归但看起来不像是这样。
我是不是漏掉了什么?
代码示例...
function Test-Recursion {
Param (
[Parameter(Mandatory=$false)]
$InputObject,
[Parameter(Mandatory=$false)]
[ValidateRange(1,100)]
[Int16] $MaxDepth = 2
)
[Int16] $_NestLevel_ += 1
if ( $_NestLevel_ -gt $MaxDepth ) { return }
"<item><nest_level>$($_NestLevel_)</nest_level><max_depth>$($MaxDepth)</max_depth></item>"
& $MyInvocation.MyCommand.ScriptBlock -InputObject $InputObject -MaxDepth $MaxDepth
}
function Get-Info {
Param (
[Parameter(Mandatory=$true)]
[String] $HostName,
[Parameter(Mandatory=$true)]
[ScriptBlock] $ConvertTo
)
$some_collection | `
ForEach-Object {
@(
$_as | ForEach-Object {
& $ConvertTo -InputObject $_ -MaxDepth 3
}
) | Sort-Object
}
}
# this code fails with either...
# - excessive recursion depth
# - invalid $MaxDepth value of 0
try {
$_ps = [powershell]::Create()
$null = $_ps.AddScript(${function:Get-Info})
$null = $_ps.AddParameter('HostName', 'some_server_name')
$null = $_ps.AddParameter('ConvertTo', ${function:Test-Recursion})
$_handle = $_ps.BeginInvoke()
while ( -Not $_handle.IsCompleted ) {
Start-Sleep -Seconds 5
}
$_result = $_ps.EndInvoke($_handle)
$_result
} catch {
$_
} finally {
$_ps.Dispose()
}
# this code works as expected
$items = Get-Info -HostName 'some_server_name' -ConvertTo ${function:Test-Recursion}
$items.table_data
我不会详细说明您希望使用这些功能完成什么,只会指出问题所在,您的 powershell
instance 是 运行由于 无限循环 导致的堆栈内存由您的 Test-Recursion
函数导致 不知道何时停止 ,因此添加了 新参数 ([Int16] $NestingLevel
) 将作为参数传递给 每次递归调用:
function Test-Recursion {
Param (
[Parameter(Mandatory = $false)]
$InputObject,
[Parameter(Mandatory = $false)]
[ValidateRange(1,100)]
[Int16] $MaxDepth = 2,
[Parameter(DontShow)]
[Int16] $NestingLevel
)
[Int16] $NestingLevel += 1
if ( $NestingLevel -gt $MaxDepth ) {
return
}
"<item><nest_level>$NestingLevel</nest_level><max_depth>$MaxDepth</max_depth></item>"
Test-Recursion -InputObject $InputObject -MaxDepth $MaxDepth -NestingLevel $NestingLevel
}
Test-Recursion -InputObject hello -MaxDepth 3
还值得注意的是,您的递归函数可以使用 while
循环进行简化:
function Test-Recursion {
Param (
[Parameter(Mandatory = $false)]
$InputObject,
[Parameter(Mandatory = $false)]
[ValidateRange(1,100)]
[Int16] $MaxDepth = 2,
[Parameter(DontShow)]
[Int16] $NestingLevel
)
while($NestingLevel++ -lt $MaxDepth) {
"<item><nest_level>$NestingLevel</nest_level><max_depth>$MaxDepth</max_depth></item>"
}
}
关于您的最新评论:
The root call initializes the variable to 1. Recursive calls would increment the variable and effectively provide a way to check the current nesting level.
不,变量在函数的每次调用中都存在,然后增量值在递归调用后丢失。但是,您可以在 $script:
scope 中初始化变量,然后它将起作用:
[Int16] $script:_NestLevel_ += 1
就个人而言,我不喜欢在我的函数中使用 $script:
作用域变量,我认为有更好的方法来做到这一点。
一个简单的演示方法是修改函数以在每次调用时输出 $_NestLevel_
,as-is,您会看到控制台显示无限量的 1
.
function Test-Recursion {
Param (
[Parameter(Mandatory = $false)]
$InputObject,
[Parameter(Mandatory = $false)]
[ValidateRange(1,100)]
[Int16] $MaxDepth = 2
)
[Int16] $_NestLevel_ += 1
if ( $_NestLevel_ -gt $MaxDepth ) { return }
$_NestLevel_
& $MyInvocation.MyCommand.ScriptBlock -InputObject $InputObject -MaxDepth $MaxDepth
}