listpowershellloopscommandrunspace

How to use runspaces in PowerShell with commands in a list of strings variable


How do you loop a command from a list of strings to use RunSpaces in PowerShell.

Here is my code for a project I am working on.

It works with normal commands but I think it does not work with the following in [void]$PowerShell.AddScript part. $singlefunction is a string variable that contains command with switch parameter sometimes like CleanUp -All:

iex -Command $singlefunction

Here is the code

# Loop for runspaces
foreach ($singlefunction in $listoffunctions)
{
    $PowerShell = [PowerShell]::Create() 
    $PowerShell.RunspacePool = $RunspacePool

    [void]$PowerShell.AddScript({       
        iex -Command $singlefunction
    })
    
    ## start runspace asynchronously so that it does not block this loop
    $handle = $PowerShell.BeginInvoke()

    # Create job
    $job = [pscustomobject]@{ 
        PowerShell = $PowerShell
        Handle = $handle
    }

    # Add first runspace to job list
    $jobs.Add( $job )

}

The functions are added to the initial session state through:

ForEach($function in $scriptFunctions)
{
    $Definition = $null
    $Definition = Get-Content -Path Function:\$function -ErrorAction Continue
    if( $Definition )
    {
        $SessionStateFunction = New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList $function , $Definition
        $SessionState.Commands.Add( $SessionStateFunction )     
    }
}

Solution

  • Based on guessing, the cause of the issue is that you're not passing $singlefunction as argument, to your runspace:

    [void] $PowerShell.AddScript({
        # `$singlefunction` does not exist in this context
        Invoke-Expression -Command $singlefunction
    })
    

    If you want to pass that string as argument to Invoke-Expression you can use .AddArgument or .AddParameter or .AddParameters.

    For instance:

    [void] $PowerShell.AddScript({
        Invoke-Expression -Command $args[0]
    }).AddArgument($singlefunction)
    

    However, if your intent is to execute the expression in that string this approach is just an over complication, .AddScript takes a string that will be evaluated as a expression, solving the issue could be as simple as:

    [void] $PowerShell.AddScript($singlefunction)
    

    Demo:

    using namespace System.Management.Automation.Runspaces
    
    $cleanUpFunc = {
        param([switch] $All, $Foo, $Bar)
    
        "Called '$($MyInvocation.MyCommand.Name)' with parameters:",
        $PSBoundParameters
    }
    
    $iss = [initialsessionstate]::CreateDefault2()
    $iss.Commands.Add([SessionStateFunctionEntry]::new(
            'CleanUp', $cleanUpFunc))
    
    $singlefunction = 'CleanUp -All -Foo Hello -Bar World'
    $rs = [runspacefactory]::CreateRunspace($iss)
    $rs.Open()
    
    $PowerShell = [PowerShell]::Create().
        AddScript($singlefunction)
    $PowerShell.Runspace = $rs
    $PowerShell.Invoke()
    
    # Outputs:
    #
    # Called 'CleanUp' with parameters:
    #
    # Key Value
    # --- -----
    # All True
    # Foo Hello
    # Bar World