【问题标题】:Powershell: Call functions outside scriptblockPowershell:在脚本块外调用函数
【发布时间】:2012-08-03 18:35:26
【问题描述】:

我正在阅读这篇关于将函数传递到脚本块以用于作业的帖子:

Powershell start-job -scriptblock cannot recognize the function defined in the same file?

我通过将函数作为变量传递来了解它的工作原理,它适用于简单的示例。那么现实世界的解决方案呢,有没有更优雅的方法来处理这个问题?

我有用于将更改部署到供应商软件的脚本。它读取一个 xml,告诉它如何导航环境并执行各种任务,即:映射驱动器、停止服务、调用 perl 安装脚本。我想为脚本提供一个参数以允许它同时运行,这样如果 perl 脚本需要 5 分钟(并不少见)并且您正在推出 11 台服务器,您无需等待脚本运行一小时。

我只是要发布一些 sn-ps,因为完整的脚本有点冗长。日志功能:

function Log
{
    Param(
    [parameter(ValueFromPipeline=$true)]
    $InputObject,
    [parameter()]
    [alias("v")]
    $verbosity = $debug    
    )       

    $messageIndex = [array]::IndexOf($verbosityArray, $verbosity)
    $verbosityIndex = [array]::IndexOf($verbosityArray, $loggingVerbosity)

    if($messageIndex -ge $VerbosityIndex)
    {
        switch($verbosity)
        {
            $debug {Write-Host $verbosity ": " $InputObject}
            $info  {Write-Host $verbosity ": " $InputObject}
            $warn  {Write-Host $verbosity ": " $InputObject -ForegroundColor yellow}
            $error {Write-Host $verbosity ": " $InputObject -ForegroundColor red}
        }        
    }
}

这是另一个调用日志函数的函数:

function ExecuteRollout
{
    param(
    [parameter(Mandatory=$true)]
    [alias("ses")]
    $session,
    [parameter(Mandatory=$true)]
    $command
    )

    #invoke command    
    Invoke-Command -session $session -ScriptBlock {$res = cmd /v /k `"$args[0]`"} -args $command

    #get the return code from the remote session
    $res = Invoke-Command -session $session {$res}
    Log ("Command Output: "+$res)    
    $res = [string] $res        
    $exitCode = $res.substring($res.IndexOf("ExitCode:"), 10)
    $exitCode = $exitCode.substring(9,1)     
    Log ("Exit code: "+$exitCode)

    return $exitCode
}

最后是我的 main 中的一个 sn-p,这样您就可以了解发生了什么。 $target.Destinations.Destination 将包含部署将转到的所有服务器和有关它们的相关信息。我删除了一些变量设置和日志记录以使其更紧凑,所以是的,您会看到从未定义过的引用变量:

#Execute concurrently
$target.Destinations.Destination | %{
    $ScriptBlock = {

        $destination = $args[0]

        Log -v $info ("Starting remote session on: "+$destination.Server)
        $session = New-PSSession -computerName $destination.Server               

        $InitializeRemote -session $session -destination $destination

        #Gets a little tricky here, we need to keep the cmd session so it doesn't lose the sys vars set by env.bat
        #String everything together with &'s    
        $cmdString = $destDrive + ": & call "+$lesDestDir+"data\env.bat & cd "+$rolloutDir+" & perl ..\JDH-rollout-2010.pl "+$rollout+" NC,r:\les & echo ExitCode:!errorlevel!"
        Log ("cmdString: "+$cmdString)
        Log -v $info ("Please wait, executing the rollout now...")
        $exitCode = $ExecuteRollout -session $session -command $cmdString    
        Log ("ExitCode: "+$exitCode)

        #respond to return code from rollout script
        $HandleExitCode -session $session -destination $destination -exitCode $exitCode

        $CleanUpRemote -session $session -destination $destination            
    }
    Start-Job $ScriptBlock -Args $_
}

因此,如果我采用链接中的方法,我会将所有函数转换为变量并将它们传递给脚本块。目前,我的日志函数将默认登录 DEBUG,除非详细参数显式传递为不同的详细程度。如果我将我的函数转换为变量,但是 powershell 似乎不喜欢这种语法:

$Log ("Print this to the log")

所以我想我现在需要一直使用这个参数:

$Log ("Print this to the log" -v $debug

因此,看起来我只需要将所有函数作为变量传递给脚本块,并在调用它们时更改一些格式。这不是一个巨大的努力,但我想知道在我开始破解我的脚本之前是否有更好的方法。感谢您的意见和关注,我知道这是一篇很长的帖子。

【问题讨论】:

    标签: powershell concurrency


    【解决方案1】:

    我开始了另一篇关于将参数传递给存储为变量的函数的帖子,这个问题的答案也解决了这个问题。这篇文章可以在这里找到:

    Powershell: passing parameters to functions stored in variables

    简短的回答是,如果您将所有函数包装在一个块中并将其存储在一个变量中,您可以使用 Start-Job 的初始化脚本参数来提供所有函数。

    例子:

    # concurrency
    $func = {
        function Logx 
        {
            param(
            [parameter(ValueFromPipeline=$true)]
            $msg
            )    
            Write-Host ("OUT:"+$msg)
        }
    }
    
    # Execution starts here
    cls
    $colors = @("red","blue","green")
    $colors | %{
        $scriptBlock = 
        {   
            Logx $args[0]
            Start-Sleep 9        
        } 
    
        Write-Host "Processing: " $_
        Start-Job -InitializationScript $func -scriptblock $scriptBlock -args $_
    }
    
    Get-Job
    
    while(Get-Job -State "Running")
    {
        write-host "Running..."
        Start-Sleep 2
    }
    
    # Output
    Get-Job | Receive-Job
    
    # Cleanup jobs
    Remove-Job * 
    

    【讨论】:

      猜你喜欢
      • 2018-02-26
      • 2022-01-26
      • 2019-08-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多