When you use a ScriptBlock
as an argument to powershell.exe
, variables aren't going to be evaluated until after the new session starts. $targetPath
has not been set in the child PowerShell process called by Workmanager.ps1
and so it has no value. This is actually an expected behavior of a ScriptBlock
in general and behaves this way in other contexts too.
The solution is mentioned in the help text for powershell -?
:
[-Command { - | <script-block> [-args <arg-array>] <========== THIS GUY
| <string> [<CommandParameters>] } ]
You must provide the -args
parameter which will be passed to the ScriptBlock
on execution (separate multiple arguments with a ,
). Passed arguments are passed positionally, and must be referenced as though you were processing the arguments to a function manually using the $args
array. For example:
$name = 'Bender'
& powershell { Write-Output "Hello, $($args[0])" } -args $name
However, especially with more complicated ScriptBlock
bodies, having to remember which index of $args[i]
contains the value you want at a given time is a pain in the butt. Luckily, we can use a little trick with defining parameters within the ScriptBlock
to help:
$name = 'Bender'
& powershell { param($name) Write-Output "Hello, $name" } -args $name
This will print Hello, Bender
as expected.
Some additional pointers:
The ScriptBlock
can be multiline as though you were defining a function. way. The examples above are single line due to their simplicity.
A ScriptBlock
is just an unnamed function, which is why defining parameters and referencing arguments within one works the same way.
To exemplify this behavior outside of powershell.exe -Command
, Invoke-Command
requires you to pass variables to its ScriptBlock
in a similar fashion. Note however that answer uses an already-defined function body as the ScriptBlock
(which is totally valid to do)
You don't need to use Start-Process
here (start
is its alias), at least as demonstrated in your example. You can simply use the call operator &
unless you need to do something more complex than "run the program and wait for it to finish". See this answer of mine for more information.
If you opt to pass a string
to powershell.exe
instead, you don't need to provide arguments and your variables will get rendered in the current PowerShell process. However, so will any other unescaped variables that might be intended to set within the child process, so be careful with this approach. Personally, I prefer using ScriptBlock
regardless, and just deal with the extra parameter definition and arguments.
Using the call &
operator is optional when you are not executing a path rendered as a string. It can be omitted in the examples above, but is more useful like so:
& "C:\The\Program Path\Contains\spaces.exe"
& $programPathAsAVariable