最近常在寫自動化及半自動化工作腳本,愈發覺得 PowerShell 好用,尤其能無縫整合 .NET 程式庫這點讓它威力無極限。但也開始學到一些實用但我之前沒學過的用法,會在 PowerShell FAQ 系列陸續整理分享。

【問題】PowerShell Function 如何接收外部函式作為輸入參數,並在函式內呼叫?

用 C# 比喻,就是指下列 DoJob 方法的 Action<string> reportProgress 參數:

public class Job 
{
    public string JobId { get; set; }
    public Job(string jobId)
    {
        JobId = jobId;
    }
    public void Execute() 
    {
        Console.WriteLine($"Job [{JobId}] is executing...");
    }
}


static void DoJob(Job[] jobs, Action<string> reportProgress)
{
    int c = 0;
    jobs.ToList().ForEach(job =>
    {
        job.Execute();
        c++;
        reportProgress($"{c}/{jobs.Length} done");
    });
}

static void Main(string[] args)
{
    var jobs = new Job[]
    {
        new Job("Coding"),
        new Job("Build"),
        new Job("Deploy")
    };
    DoJob(jobs, (m) =>
    {
        Console.WriteLine(m);
    });
    Console.ReadLine();
}

以下是 PowerShell 傳入 Callback 函式的範例:(連 C# Job Class 部分也試著用 PowerShell 實作,建構式、屬性、方法,PowerSehll 的類別都支援,一點不含糊,是可以認真應用的好語言)
補充:Class 要 PowerShell 5.0+,Windows Server 2016+ 才內建,Windows 2012R2 需另外安裝 Windows Management Framework 5.1 參考

function ShowProgress() {
    param ([string] $msg)
    Write-Host $msg
}

class Job {
    [string]$JobId
    Job([string]$jobId) {
        $this.JobId = $jobId
    }
    Execute() {
        Write-Host "Job [$($this.JobId)] is executing..."
    }
}

function DoJobs([Job[]] $jobs, [scriptblock] $reportProgress) {
    $c = 0;
    $jobs | ForEach-Object {
        $_.Execute()
        $c++;
        $reportProgress.Invoke("$c/$($jobs.Length) done")
    }

}

$jobs = (
    [Job]::new("Coding"),
    [Job]::new("Build"),
    [Job]::new("Deploy")
)

DoJobs $jobs $Function:ShowProgress

有三個關鍵處:

  1. Callback 參數型別請用 [ScriptBlock]
  2. 呼叫 Callback 時用 .Invoke
  3. 傳入 [ScriptBlock] 時寫成 $Function:函式名稱

用 Visual Studio Code 開發及測試 PowerShell,整體體驗順手流暢,推。實測結果如下,成功!

等等,C# 裡是用 Lambda 表示式提供 reportProgress,PowerShell 行不行?當然沒問題:

DoJobs $jobs { Param($m) Write-Host $m }

發現目前版本的 VSCode PowerShell (v2020.4.0) 解析上述寫法時會彈出 Missing closing '}' in statement block or type definition. 警告,推測是 Bug,但不影響執行。

Tips of how to pass callback parameters to PowerShell function.


Comments

Be the first to post a comment

Post a comment