在工作流旁边使用递归函数

时间:2017-07-19 07:30:06

标签: powershell recursion powershell-workflow

我有一个PowerShell脚本,其目的是获取文件列表,然后对每个文件执行一些操作。

文件列表由递归函数生成,如下所示:

function Recurse($path)
{
    .. create $folder

    foreach ($i in $folder.files) { 
        $i
    }
    foreach ($i in $folder.subfolders) {
        Recurse($i.path)
    }
}

与此功能分开,我执行一个工作流程,该工作流程获取文件列表并对每个文件执行工作(并行)。代码看起来像这样:

workflow Do-Work {
    param(
        [parameter(mandatory)][object[]]$list
    )
    foreach -parallel ($f in $list) {
        inlinescript {
            .. do work on $Using:f
        }
    }
}

然后将这两部分与以下逻辑结合起来:

$myList = Recurse($myPath)
Do-Work -list $myList

问题是这会产生错误:

A workflow cannot use recursion.
    + CategoryInfo          : ParserError: (:) [], ParseException
    + FullyQualifiedErrorId : RecursiveWorkflowNotSupported

当递归函数和工作流分开时,为什么会发生这种情况? 有没有办法解决这个问题?

3 个答案:

答案 0 :(得分:1)

工作流程中不允许递归调用。

直接提供您的路径:

而不是:

Recurse($myPath)

这样做:

Recurse $myPath

您可以参考这篇文章:

Adding Nested functions and Nested Workflows

答案 1 :(得分:0)

我最终(当然在发布问题后几分钟)通过将函数解压缩到自己的模块来解决这个问题:

获取-files.psm1:

@{
...
FunctionsToExport = @(Recurse)
...
}

获取-files.psd1:

workflow do-work {
    params(
        [parameter(mandatory)][object[]]$files
    )
    ...
}
Import-Module -Name "path\to\module\get-files.psm1"
$files = Recurse -path $myPath

do-work -files $files

<强> script.ps1:

"../node_modules/bootstrap/dist/css/bootstrap.min.css",

这似乎使主要脚本错过了Recurse使用递归并且它有效。

答案 2 :(得分:0)

对于那些希望在模块中使用“并行”工作流(无递归)的人来说,解决方案相似但有所不同。

例如,此工作流程可用于并行启动/停止服务

Workflow Invoke-ServiceInParallelWF
{
    <#
    .SYNOPSIS
    Workflow to stop/start services in parallel on a server.
    .DESCRIPTION
    Utilizes a workflow to start/stop services running on a server in parallel to shorten the start/stop duration.
    #>
    Param(
        [Parameter(Mandatory=$true)]
        [string[]]$Name,
        [Parameter(Mandatory=$true)]
        [ValidateSet("Start","Stop")]
        [string]$Action
    )

    if (!($Name.Count -gt 0))
    {
        throw "No services provided!"
    }
    
    # run parrallel on services argument
    foreach -parallel ($svc in $Name){
    
        InlineScript{
        
            #build object first for consistency
            $props=[ordered]@{
                Service=$using:svc;
                Action=$using:action
                Result=$null
                Error=$null
            }
            
            # Wrap in exception handler
            Try{
                #Perform the desired service action
                if ($using:action -eq 'stop') {
                    Stop-Service -name $using:svc -ErrorAction stop
                } elseif ($using:action -eq 'start') {
                    Start-Service -name $using:svc -ErrorAction stop
                } else {
                    $Action='unknown'
                }
                $props.Result='Success'
            }
            Catch{
                $props.Result='Fail'
                $props.Error="$_"
            }
            
            # generate object back to workflow
            New-Object -TypeName PSCustomObject -Property $props
        }
        
    }
}

如果将其放在psm1文件中并尝试导入,它将因以下错误而失败:

At C:\Source\Powershell\Common\Modules\MyModule\MyModule.psm1:1 char:1
+ #
+ ~
A workflow cannot use recursion.
    + CategoryInfo          : ParserError: (:) [], ParseException
    + FullyQualifiedErrorId : RecursiveWorkflowNotSupported

要将其嵌入模块中,请勿将其放入.psm1文件中,创建一个单独的ps1文件并将其放置在模块文件夹中。 例如Invoke-ServiceInParallelWF.ps1

然后在清单(psd1)文件中,修改ScriptsToProcess以包括ps1文件。

@{

  # Script module or binary module file associated with this manifest.
  RootModule = 'MyModule.psm1'

  # Version number of this module.
  ModuleVersion = '1.47.1'

  # ID used to uniquely identify this module
  GUID = 'bd4390dc-a8ad-4bce-8d69-f53ccf8e4163'

  # Author of this module
  Author = 'Justin Marshall'

  # Script files (.ps1) that are run in the caller's environment prior to importing this module.
  ScriptsToProcess = @('Invoke-ServiceInParallelWF.ps1')
}

最后,导入模块并测试功能:

PS C:\source\powershell> Import-Module MyModule -force

PS C:\source\powershell> Invoke-ServiceInParallelWF -Action Start -Name w3svc

Service               : w3svc
Action                : Start
Result                : Success
Error                 :
PSComputerName        : localhost
PSSourceJobInstanceId : 1a564d5d-f363-44b7-a27e-88670764de2d