powershellparameter-passingautomatic-variable

How to pass parameters to a PS script invoked through Start-Job?


I want to use start-job to run a .ps1 script requiring a parameter. Here's the script file:

#Test-Job.ps1 
Param (
[Parameter(Mandatory=$True)][String]$input
)

$output = "$input to output"

return $output

and here is how I am running it:

$input = "input"
Start-Job -FilePath 'C:\PowerShell\test_job.ps1' -ArgumentList $input -Name "TestJob"
Get-Job -name "TestJob" | Wait-Job | Receive-Job
Get-Job -name "TestJob" | Remove-Job

Run like this, it returns " to output", so $input is null in the script run by the job.

I've seen other questions similar to this, but they mostly use -Scriptblock in place of -FilePath. Is there a different method for passing parameters to files through Start-Job?


Solution

  • tl;dr


    As Lee_Dailey notes, $input is an automatic variable and shouldn't be assigned to (it is automatically managed by PowerShell to provide an enumerator of pipeline input in non-advanced scripts and functions).

    Regrettably and unexpectedly, several automatic variables, including $input, can be assigned to: see this answer.

    $input is a particularly insidious example, because if you use it as a parameter variable, any value you pass to it is quietly discarded, because in the context of a function or script $input invariably is an enumerator for any pipeline input.

    Here's a simple example to demonstrate the problem:

    PS> & { param($input) "[$input]" } 'hi'
     # !! No output - the argument was quietly discarded.
    

    That the built-in definition of $input takes precedence can be demonstrated as follows:

    PS> 'ho' | & { param($input) "[$input]" } 'hi'
    ho # !! pipeline input took precedence
    

    While you can technically get away with using $input as a regular variable (rather than a parameter variable) as long as you don't cross scope boundaries, custom use of $input should still be avoided:

    & {
      $input = 'foo'   # TO BE AVOIDED
      "[$input]"       # Technically works: -> '[foo]'
      & { "[$input]" } # FAILS, due to child scope: -> '[]'
    }