Paul
Paul

Reputation: 1403

How to use local PowerShell variable in session using Start-Process?

I have a local string variable containing a file path that I want to define and set early on in my PS script, and then reuse in the command block of my Start-Process call. I've tried this:

Start-Process Powershell { Set-Location $FlatScriptFolder ; .\BH-Corporate-Flats-Make-Assets.ps1 ; Read-Host}

That specific line causes an error when the script runs:

Set-Location : Cannot process argument because the value of argument "path" is null. Change the value of argument
"path" to a non-null value.
At line:1 char:1
+ Set-Location $FlatScriptFolder ; .\BH-Corporate-Flats-Make-Serve.ps1  ...

I've gathered that the problem is variable scope. My variable is local, and the (remote) session cannot see it. I found this blog post which explains the issue perfectly but I don't understand how the solution would work in my case. I'm honestly having trouble researching this specific issue, so hopefully someone here can help?

Here are two other things that I've tried:

Start-Process Powershell { param ($FlatScriptFolder) Set-Location $FlatScriptFolder ; .\BH-Corporate-Flats-Make-Assets.ps1 ; Read-Host}

$Script1Command = { Set-Location $FlatScriptFolder ; .\BH-Corporate-Flats-Make-Serve.ps1 ; Read-Host}
Start-Process Powershell -ArgumentList ($Script1Command)

When I try either of the above, I get the exact same error message.

Upvotes: 2

Views: 714

Answers (1)

mklement0
mklement0

Reputation: 437278

You cannot pass a script block directly to powershell.exe when you use Start-Process[1] , but you can pass a string representation of one, which then requires you to prefix it with &, the call operator.

Also, in order to incorporate variable values from the caller's context, you need to pass them as arguments (the 1st of which can be accessed as $Args[0]):

Start-Process powershell.exe '-Command', '&',
 '{ Set-Location $Args[0]; .\BH-Corporate-Flats-Make-Assets.ps1; Read-Host }',
   "\`"$FlatScriptFolder\`""

Note the - unfortunate - need to enclose the value of the argument in doubly escaped embedded "...", which is required for values with embedded spaces to be passed correctly - see this GitHub issue.

Note: "'$FlatScriptFolder'", i.e. using single-quoting for the embedded quotes works in principle, but only if the specified value happens to contain no ' instances itself. The double escaping needed for " stems from (a) satisfying PowerShell's immediate syntax requirements for embedding double quotes in a double-quoted string (`") with additionally needing to \-escape them for Powershell's CLI to recognize them.


Alternatively, you can use string expansion (interpolation) expansion in the caller's context to "bake" local variables into the command string to pass to the new PowerShell instance:

  • Since you're then not passing any arguments, there is no need to pass a (stringified) script block preceded by the & operator - you can just pass the statement list as-is.

  • The caveat is that distinguishing what will be executed in the caller's context from what the callee ends up executing can get tricky (you'll need to `-escape $ instances that refer to variables / expressions in the callee's context).

Start-Process powershell.exe '-Command', 
 "Set-Location \`"$FlatScriptFolder\`"; .\BH-Corporate-Flats-Make-Assets.ps1 ; Read-Host"

Finally, if all you want to do is to set the working directory for the new process, you can simply use the -WorkingDirectory parameter:

Start-Process -WorkingDirectory $FlatScriptFolder powershell.exe '-Command', 
  '.\BH-Corporate-Flats-Make-Assets.ps1; Read-Host'

[1] The arguments after powershell.exe are implicitly passed to Start-Process' 2nd positional parameter,
-ArgumentList, whose type is [string[]]. Thus, any non-string type is converted to [string] on invocation. If you convert a script block ({...}) to a string, the literal contents - withotu the enclosing { and } is passed; in short: passing {...} is the same as passing '...', whatever ... represents, and the target powershell.exe instance will not see it as a script block.

Upvotes: 2

Related Questions