I'm trying this
$Global:commandBlock={
Start-Transcript -path $projectFolder\gruntLog.txt;
grunt $argList;
Stop-Transcript
}
$cmdProc=start-process powershell -ArgumentList ('-command `$Global:commandBlock') -WorkingDirectory $fwd -PassThru -NoNewWindow:$NoNewWindow
And keep getting $commandBlock : The term '$Global:commandBlock' is not recognized as the name of a cmdlet, function, script file, or operable program.
My guess was it has to do with scope. But making variable global didn't help. Adding -args $commandBlock
like that:
-ArgumentList ('-command `$Global:commandBlock -args "-commandBlock:$commandBlock"')
-ArgumentList ('-command `$Global:commandBlock -args $commandBlock"')
didn't help
And I'm not sure that I escape variables correctly in the block, read this, but not sure how to apply to my script.
There's a few things which I think are keeping this from working. First, when you're using single quotes,
'
you're instructing PowerShell to operate literally. This means that it won't expand variables. Not what you're looking for.A better way to do this is to do it with an subexpression like this.
This will give you the desired results.
Subexpressions are pretty sweet. It lets you embed a mini-scriptblock within a string, and it's then expanded out in the parent string.
I've messed around with the syntax for passing args to a new powershell instance and have found the following works. So many variations fail without a good error message. Maybe it would work in your case?
Also simply using the Invoke-Command gives you the -ArgumentList parameter to opperate against the given Command that you are missing with the standard powershell.exe parameters. This is probably a bit cleaner looking.
No need for any extra complex escaping or unwanted persisted variables. Just keep the script block in curly braces so it remains a script block on arrival in the new session. At least in this simple case...
If you have several string parameters that contain spaces. I found popping the string in a single parenthesis and separating with commas works well. You could also probably pass a predefined array as a single argument.
There are two major issues (leaving the obvious mistake of attempting to reference a variable inside a single-quoted string aside):
Any argument you want to pass to a new
powershell
instance via-Command
must be escaped in non-obvious ways if it contains"
and/or\
chars, which is especially likely if you're passing a piece of PowerShell source code.-EncodedCommand
parameter - see this answer of mine to a related question for how to do that, but a more concise alternative is presented below.If the source code being passed references any variables that only exist in the calling session, the new instance won't see them.
To solve the local-variable-not-seen-by-the-new-instance problem, we must rewrite the script block to accept parameters:
Now we can apply the necessary escaping, using PetSerAl's sophisticated
-replace
expression from his comment on the question.We can then invoke the resulting string with
& {...}
while passing it parameter values (I'm omitting the-WorkingDirectory
and-PassThru
parameters for brevity):For an explanation of the regular expression, again see this answer.
Note how the variable values passed as parameters to the script block are enclosed in
'...'
inside a"..."
-enclosed string in order to:Note: If your variable values have embedded
'
instances, you'll have to escape them as''
.The above yields:
Alternative with a temporary, self-deleting script file:
Using
-File
with a script file has the advantage of being able to pass parameter values as literals, with no concern over additional interpretation of their contents.Caveat: As of PowerShell Core v6-beta.3, there is a problem when passing parameter values that start with
-
: they are not bound as expected; see this GitHub issue.To work around this problem, the sample script block below accesses only the first parameter by name, and relies on all remaining ones binding via the automatic
$Args
variable.Again, the above yields:
Will this work: