首页 > 解决方案 > 在 PowerShell 作业中使用本地函数

问题描述

我正在编写自定义 cmdlet 来执行任务。其中一个 cmdlet 依赖于其他 cmdlet,并且需要很长时间,因此我想在作业中执行该任务。好吧,你猜怎么着,因为其他 cmdlet 在该 Job 的范围内不可用。为什么?其他语言,如 C++、Java、C# 允许您在同一范围内使用变量、对象、函数,为什么在 PowerShell 中不可用?为什么会如此脱钩?我觉得这让开发人员更难了。也许我不明白这个话题,但我想做这样的事情:

function Write-Yes {
    Write-Host "yes"
}

function Write-No {
    Write-Host "no"
}

function Write-Random {
    $result = @($true, $false) | Get-Random
    if ($result) {
        Write-Yes
    }
    else {
        Write-No
    }
}

Start-Job -ScriptBlock { Write-Random }

这是不可能的。你必须做一些技巧,比如提供函数的脚本块作为参数,并使用调用运算符或类似的东西来调用它。甚至,使用 Import-Module 重新导入您正在使用的同一文件。这感觉过于复杂。我看到的唯一能够执行此类操作的模块是 PoshRSJob,它允许您命名将在作业中使用的 cmdlet,它会为您动态创建它们,并进行一些词法解析,再一次,过于复杂的事情。

为什么事情会像现在这样,有什么办法可以以优雅的方式做我在示例中尝试的事情吗?

标签: powershell

解决方案


Start-Job 非常有限且缓慢。恕我直言,它的实施非常糟糕,我从不使用它。您可以将运行空间用于快速和轻量级的“后台作业”,并从当前会话中导入函数和变量。

例子:

function Write-Yes { "yes" }

function Write-No { "no" }

function Write-Random {
    if ($true, $false | Get-Random) {
        Write-Yes
    }
    else {
        Write-No
    }
}

# setup session and import functions
$session = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
"Write-Yes", "Write-No", "Write-Random" | foreach {
    $session.Commands.Add((
        New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry $_, (Get-Content "Function:\$_")
    ))
}

# setup separate powershell instance
$job = [Powershell]::Create($session)
[void]$job.AddScript({ Write-Random })

# start async
$asyncResult = $job.BeginInvoke()

# do stuff ...

# wait for completion
$job.EndInvoke($asyncResult)
$job.Dispose()

但总的来说,Powershell 不是为复杂的并行处理而设计的。通常,最好将所有内容放在脚本文件中并作为任务或后台作业等运行。


推荐阅读