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.
$Global:commandBlock={
'ham' >> C:\temp\test.txt
}
$cmdProc=start-process powershell -ArgumentList ("-command $($Global:commandBlock)") -PassThru -NoNewWindow:$NoNewWindow
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.
"today's date is $(get-date), on system: $($env:COMPUTERNAME)"
today's date is 02/14/2017 11:50:49, on system: BEHEMOTH
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 :
$scriptBlock={
param($projectFolder, $argList)
# For demonstration, simply *output* the parameter values.
"folder: [$projectFolder]; arguments: [$argList]"
}
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):
# Parameter values to pass.
$projectFolder = 'c:\temp'
$argList='-v -f'
Start-Process -NoNewWindow powershell -ArgumentList '-noprofile', '-command',
(('& {' + $scriptBlock.ToString() + '}') -replace '\"|\\(?=\\*("|$))', '\$&'),
"'$projectFolder'",
"'$argList'"
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:
folder: [c:\temp]; arguments: [-v -f]
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.
# Define the script block to be executed by the new PowerShell instance.
$scriptBlock={
param($projectFolder)
# For demonstration, simply *output* the parameter values.
"folder: [$projectFolder]; arguments: [$Args]"
}
# Parameter values to pass.
$projectFolder = 'c:\temp'
$argList='-v -f'
# Determine the temporary script path.
$tempScript = "$env:TEMP\temp-$PID.ps1"
# Create the script from the script block and append the self-removal command.
# Note that simply referencing the script-block variable inside `"..."`
# expands to the script block's *literal* content (excluding the enclosing {...})
"$scriptBlock; Remove-Item `$PSCommandPath" > $tempScript
# Now invoke the temporary script file, passing the arguments as literals.
Start-Process -NoNewWindow powershell -ArgumentList '-NoProfile', '-File', $tempScript,
$projectFolder,
$argList
Again, the above yields:
folder: [c:\temp]; arguments: [-v -f]
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?
$arg = "HAM"
$command = {param($ham) write-host $ham}
#please not its important to wrap your command
#in a further script block to stop it being processed to a string at execution
#The following would normally suffice "& $command $arg"
Start-Process powershell -ArgumentList "-noexit -command & {$command} $arg"
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.
Start-Process powershell -ArgumentList "-noexit -command invoke-command -scriptblock {$command} -argumentlist $arg"
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.
Start-Process powershell -ArgumentList "-noexit -command invoke-command -scriptblock {$command} -argumentlist '$arg1', '$arg2', '$arg3'"
Will this work:
$Global:commandBlock={
Start-Transcript -path $projectFolder\gruntLog.txt;
grunt $argList;
Stop-Transcript
}
& $Global:commandBlock
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.