从PowerShell函数调用回调

时间:2017-11-22 16:46:33

标签: powershell

我正在尝试为PowerShell函数添加一些回调功能,但我希望能够在函数定义之外定义回调的行为,所以基本上是一个非常简单的PowerShell控制反转。

我正在尝试尽可能向后兼容,这是我到目前为止所提出的(为简洁起见,错误处理已被删除):

$ErrorActionPreference = "Stop";
Set-StrictMode -Version "Latest";

function Invoke-DoSomethingMethod
{
    param
    (
        [hashtable] $Callbacks
    )
    $CallBacks["OnStarted"].Invoke();
    for( $i = 1; $i -le 10; $i++ )
    {
        # ... do something here ...
        $CallBacks["OnProgress"].Invoke($i * 10);
    }
    $CallBacks["OnFinished"].Invoke();
}

$myHostCallbacks = @{
    "OnStarted"  = { write-host "started" }
    "OnProgress" = { write-host "processing $($args[0])%" }
    "OnFinished" = { write-host "finished" }
}

$myFileCallbacks = @{
    "OnStarted"  = { Add-Content "C:\temp\log.txt" "started" }
    "OnProgress" = { Add-Content "C:\temp\log.txt" "processing $($args[0])%" }
    "OnFinished" = { Add-Content "C:\temp\log.txt" "finished" }
}

$myWebCallbacks = @{
    "OnStarted"  = { Invoke-WebRequest -Uri "http://example.org/OnStarted" }
    "OnProgress" = { Invoke-WebRequest -Uri "http://example.org/OnProgress/$($args[0])" }
    "OnFinished" = { Invoke-WebRequest -Uri "http://example.org/OnFinish" }
}

Invoke-DoSomethingMethod -Callbacks $myHostCallbacks;

运行时,输出以下内容:

c:\temp>powershell .\callbacks.ps1

started
processing 10%
processing 20%
processing 30%
processing 40%
processing 50%
processing 60%
processing 70%
processing 80%
processing 90%
processing 100%
finished

在不使用外部模块或C#代码的情况下,是否有一种更为自觉的方式使用PowerShell,而不依赖于匹配回调名称的魔术字符串和$ args []参数的顺序? (优先与2.0版兼容)。

干杯,

麦克

更新

对于某些上下文,我的Invoke-DoSomething函数是CI构建框架的一部分 (https://github.com/ASOS/OctopusStepTemplateCi)当前是硬编码的,用于在构建过程中使用Write-Host发送TeamCity服务消息。

我们有兴趣支持其他构建服务器产品,因此我正在研究一种方法,将与构建服务器的各种交互抽象为用户可定义的回调,如“OnBuildStarted”,“OnBuildStepFailed”等,就像你可能做的那样使用C#中的IoC框架。

这样可以更轻松地从项目内部为新的构建服务器提供支持,也可以让用户为不是开箱即用的构建服务器编写回调。

这可能有助于说明我正在尝试做的事情:

function Invoke-BuildScript
{
    param( [hashtable] $Callbacks )
    $CallBacks["OnBuildStarted"].Invoke();
    ... build script here ...
    $CallBacks["OnBuildFinished"].Invoke();
}

$teamCityCallbacks = @{
    "OnBuildStarted"  = { write-host "started" }
    "OnBuildFinished" = { write-host "finished" }
    "OnBuildError"    = { write-host "error '$($args[0])'" }
}

$jenkinsCallbacks = @{
    "OnBuildStarted"  = { ... }
    "OnBuildFinished" = { ... }
    "OnBuildError"    = { ... }
}

Invoke-BuildScript -Callbacks $teamCityCallbacks;

2 个答案:

答案 0 :(得分:2)

与我在PowerShell中关于IoC的原始问题的答案不同,但我设法至少隐藏了一些包装器cmdlet背后的魔术字符串和回调参数,因此回调结构变为不透明类型:

# knowledge of magic strings and parameter orders confined to here

function Invoke-OnStartedCallback
{
    param
    (
        [hashtable] $Callbacks
    )
    Invoke-Callback -Callbacks $CallBacks -Name "OnStarted";
}

function Invoke-OnProgressCallback
{
    param
    (
        [hashtable] $Callbacks,
        [int]       $PercentComplete
    )
    Invoke-Callback -Callbacks $CallBacks -Name "OnProgress" -Parameters @( $PercentComplete );
}

function Invoke-OnFinishedCallback
{
    param
    (
        [hashtable] $Callbacks
    )
    Invoke-Callback -Callbacks $CallBacks -Name "OnFinished";
}

function New-DoSomethingCallbacks
{
    param
    (
        [scriptblock] $OnStarted,
        [scriptblock] $OnProgress,
        [scriptblock] $OnFinished
    )
    return @{
        "OnStarted"  = $OnStarted
        "OnProgress" = $OnProgress
        "OnFinished" = $OnFinished
    };
}

其余代码可以写成:

# no knowledge of magic strings below here

function Invoke-DoSomethingMethod
{
    param
    (
        [hashtable] $Callbacks
    )
    Invoke-OnStartedCallback $CallBacks;
    for( $i = 1; $i -le 10; $i++ )
    {
        # ... do something ...
        Invoke-OnProgressCallback $CallBacks -PercentComplete ($i * 10);
    }
    Invoke-OnFinishedCallback $CallBacks;
}


$myHostCallbacks = New-DoSomethingCallbacks -OnStarted  { write-host "started" } `
                                            -OnProgress { write-host "processing $($args[0])%" } `
                                            -OnFinished { write-host "finished" };

$myFileCallbacks = New-DoSomethingCallbacks -OnStarted  { Add-Content "C:\temp\log.txt" "started" } `
                                            -OnProgress { Add-Content "C:\temp\log.txt" "processing $($args[0])%" } `
                                            -OnFinished { Add-Content "C:\temp\log.txt" "finished" };

$myWebCallbacks  = New-DoSomethingCallbacks -OnStarted  { Invoke-WebRequest -Uri "http://example.org/OnStarted" } `
                                            -OnProgress { Invoke-WebRequest -Uri "http://example.org/OnProgress/$($args[0])" } `
                                            -OnFinished { Invoke-WebRequest -Uri "http://example.org/OnFinish" };

Invoke-DoSomethingMethod -Callbacks $myHostCallbacks;

使用小帮助cmdlet:

function Invoke-Callback
{
    param
    (
        [hashtable] $Callbacks,
        [string]    $Name,
        [object[]]  $Parameters
    )
    if( -not $Callbacks.ContainsKey($Name) )
    {
        return;
    }
    $callback = $Callbacks[$Name];
    if( $null -eq $callback )
    {
        return;
    }
    if( $null -eq $Parameters )
    {
        $callback.Invoke();
    }
    else
    {
        $callback.Invoke($Parameters);
    }
}

答案 1 :(得分:0)

不确定以下是否符合您的要求。也许我也误解了这个问题。

function Invoke-DoSomethingMethod ($OnStarted, $OnProgress, $OnFinished) {
    Invoke-Command -ScriptBlock $OnStarted
    for ( $i = 1; $i -le 10; $i++ ) {
        Invoke-Command -ScriptBlock $OnProgress
    }
    Invoke-Command -ScriptBlock $OnFinished
}

$OnStartedSb = [ScriptBlock]::Create('write-host "started"')
$OnProgressSb = [ScriptBlock]::Create('write-host "processing $($i * 10)%"')
$OnFinishedSb = [ScriptBlock]::Create('write-host "finished"')
Invoke-DoSomethingMethod -OnStarted $OnStartedSb -OnProgress $OnProgressSb -OnFinished $OnFinishedSb