将参数块添加到脚本块

Add Param block to Scriptblock

我正在使用 this script,如下所示,等待创建邮箱,但我想抑制生成的错误对话框,因为我不需要收到邮箱尚未创建的通知,因为 Wait-Action 函数会通知用户它正在等待操作完成

<#PSScriptInfo
    .VERSION 1.0
    .GUID 389989f2-626a-45cc-aa5c-2df2f93cee03
    .AUTHOR Adam Bertram
    .COMPANYNAME Adam the Automator, LLC
    .PROJECTURI https://github.com/adbertram/Random-PowerShell-Work/blob/master/PowerShell%20Internals/Wait-Action.ps1
#>

<#
    .SYNOPSIS
        A script to wait for an action to finish.
 
    .DESCRIPTION
        This script executes a scriptblock represented by the Condition parameter continually while the result returns
        anything other than $false or $null.
 
    .PARAMETER Condition
         A mandatory scriptblock parameter representing the code to execute to check the action condition. This code
         will be continually executed until it returns $false or $null.
     
    .PARAMETER Timeout
         A mandatory integer represneting the time (in seconds) to wait for the condition to complete.
 
    .PARAMETER ArgumentList
         An optional collection of one or more objects to pass to the scriptblock at run time. To use this parameter,
         be sure you have a param() block in the Condition scriptblock to accept these parameters.
 
    .PARAMETER RetryInterval
         An optional integer representing the time (in seconds) between the code execution in Condition.
 
    .EXAMPLE
        PS> Wait-Action -Condition { (Get-Job).State | where { $_ -ne 'Running' } -Timeout 10
         
        This example will wait for all background jobs to complete for up to 10 seconds.
#>

[OutputType([void])]
[CmdletBinding()]
param
(
    [Parameter(Mandatory)]
    [ValidateNotNullOrEmpty()]
    [scriptblock]$Condition,

    [Parameter(Mandatory)]
    [ValidateNotNullOrEmpty()]
    [int]$Timeout,

    [Parameter()]
    [ValidateNotNullOrEmpty()]
    [object[]]$ArgumentList,

    [Parameter()]
    [ValidateNotNullOrEmpty()]
    [int]$RetryInterval = 5
)
try
{
    $timer = [Diagnostics.Stopwatch]::StartNew()
    while (($timer.Elapsed.TotalSeconds -lt $Timeout) -and (& $Condition $ArgumentList)) {
        Start-Sleep -Seconds $RetryInterval
        $totalSecs = [math]::Round($timer.Elapsed.TotalSeconds,0)
        Write-Verbose -Message "Still waiting for action to complete after [$totalSecs] seconds..."
    }
    $timer.Stop()
    if ($timer.Elapsed.TotalSeconds -gt $Timeout) {
        throw 'Action did not complete before timeout period.'
    } else {
        Write-Verbose -Message 'Action completed before timeout period.'
    }
}
catch
{
    Write-Error -Message $_.Exception.Message
}

在我的脚本中,此行从上方调用 Wait-Action 函数:

Write-Host ('>> Creating ' + $user_upn + '''s mailbox, please be patient...') -ForegroundColor Yellow
Wait-Action -Condition { (Get-Mailbox -Identity $user_upn) } -Timeout 300 -ArgumentList -ErrorAction SilentlyContinue

我想把-ArgumentList -ErrorAction SilentyContinue放在(Get-Mailbox -Identity $user_upn <here>)里面,但是我无法理解原作者在解释ArgumentList参数的用法时的意思。有谁能解释一下吗?

.PARAMETER ArgumentList An optional collection of one or more objects to pass to the scriptblock at run time. To use this parameter, be sure you have a param() block in the Condition scriptblock to accept these parameters.

这里有一个 -ArgumentList 参数的用法示例:

$jobs = 0..2 | ForEach-Object { Start-Job { Start-Sleep -Seconds 120 } }

Wait-Action -Condition {param($s) $s.State -contains 'Running'} -Timeout 10 -ArgumentList $jobs -Verbose
# => Will throw after 10 seconds: 
#    Wait-Action : Action did not complete before timeout period.

$jobs | Stop-Job -PassThru | Remove-Job

$jobs 变量中存储 3 个将 运行 持续 2 分钟的作业,然后使用 -ArgumentList 我们将 PSRemotingJob 对象的集合传递给函数。然后,条件块检查通过 ArgumentList 传递的集合的 State 属性 的值是否为 Running.

根据上面的示例,如果您将 Jobs 睡眠计时器更改为低于 Wait-Action-TimeOut,只要使用 -Verbose,您应该会看到类似这样的内容:

VERBOSE: Still waiting for action to complete after [5] seconds...
VERBOSE: Action completed before timeout period.

请注意,作者建议在 -Condition 块中使用 param() 块,即使推荐,$args 也应该有效:

-Condition {$args.State -contains 'Running'}

在这种情况下,以及支持 -ArgumentList 参数(例如 Start-Job)的 built-in cmdlet,以下 注意事项和限制 适用于 pass-through 个参数:

  • 只能传递位置参数,作为单个数组

    的元素
  • 出于句法原因,任何看起来像参数名称(例如-foo)的元素都必须用引号,这样它就不会被误认为是手头命令的参数。

  • pass-through 参数的目标命令必须设计为接受传递的位置参数并对其进行操作。在最简单的情况下,您可以使用 automatic $args variable in your -Condition script block ({ ... }),但是脚本块支持 param(...) 形式参数声明的声明,就像函数和脚本一样,这总是可取的 - 和这就是 Wait-Action 在您的问题中的帮助所暗示的内容;底部部分显示了如何使用这些正式声明的参数。


换句话说:

  • 您不能将 -ErrorAction SilentlyContinue 传递给 -ArgumentList:它不是单个数组,并且 -ErrorAction 将被解释为属于 Wait-Action本身

  • 但即使您解决了这些问题并传递了 '-ErrorAction', SilentlyContinue,目标脚本块也会将 -ErrorAction 视为 positional 参数逐字值-ErrorAction.

  • 无论您最终传递给 -ArgumentList 的任何位置参数,您都必须在 -Condition 脚本块中通过自动 $args 数组或参数进行操作通过 param(...)

    预先声明

解决方案:

  • 按位置传递所有pass-through参数并在接收脚本块中声明匹配参数。

    Wait-Action -Condition { 
      param($ErrorAction) 
      if (-not $ErrorAction) { $ErrorAction = $ErrorActionPreference }
      Get-Mailbox -ErrorAction $ErrorAction -Identity $user_upn
    } -Timeout 300 -ArgumentList SilentlyContinue
    
  • 通过接收脚本块可以解释的 单个哈希表 传递 pass-through 参数(就像您对 splatting 所做的那样)或用于飞溅。

    Wait-Action -Condition { 
      param($paramsHash) 
      if (-not $paramsHash) { $paramsHash = @{} }
      Get-Mailbox @paramsHash -Identity $user_upn
    } -Timeout 300 -ArgumentList @{ ErrorAction = 'SilentlyContinue' }
    

请注意,这两种解决方法都考虑了在给定的 Wait-Action 调用中 没有传递 参数的情况(即当 -ArgumentList 参数是 省略).

勾选About Scriptblocks

示例 1

$sb = {
    param($p1,$p2)
    "p1 is $p1, p2 is $p2, rest of args: $args"
}
Invoke-Command $sb -ArgumentList 1,2,3,4

输出p1 is 1, p2 is 2, rest of args: 3 4

示例 2

Invoke-Command { Write-Host ($args[0] + " " + $args[1] + " - AllArgs: $args") } -ArgumentList "Hello!!", "Joma", 1, 2, 3

输出Hello!! Joma - AllArgs: Hello!! Joma 1 2 3