Trying to run simple http server in separate process using powershell. But can't figure out how to pass parameters to it. Tried several approaches and constantly getting exception. Could you advice?
$port = 9700
$prefix = 'http://+:{0}/' -f $port;
$ScriptBlock = {
$http = [System.Net.HttpListener]::new()
$http.Prefixes.Add($args[0]);
$http.Start();
$Error.Clear(); }
Start-Process pwsh -ArgumentList '-NoProfile', '-NoExit', "-Command" $ScriptBlock $prefix -NoNewWindow -Wait
In result of example above it: A positional parameter cannot be found that accepts argument ' $http = [System.Net.HttpListener]::new() | $http.Prefixes.Add($args[0]); $http.Start(); $Error.Clear(); '.
All pass-through arguments passed to Start-Process
must be the elements of a single array passed to -ArgumentList
.
$ScriptBlock
and $prefix
in your call aren't part of the -ArgumentList
array and therefore constitute additional, positional arguments from Start-Process
's perspective, which it doesn't recognize - that caused the error you saw.Fundamentally, all pass-through arguments are invariably strings, so you cannot pass a script block as such (it would be turned into its verbatim source code, not including the enclosing {
and }
)
However, since are looking for synchronous invocation (-Wait
) in the same window (-NoNewWindow
), there's no need for Start-Process
at all, and you can simply call pwsh
directly, which - from inside a PowerShell session ony - does allow you to use a script block:
pwsh -NoProfile -NoExit $ScriptBlock -args $prefix
See the docs for PowerShell (Core)'s CLI, pwsh
.
If you do want to use Start-Process
- which is only necessary if you want to run the code in a new window or with elevation (as admin) or with a different user identity - use the following:
For readability and ease of embedding quotes, the solution uses an expandable here-string (@"<newline>...<newline>@"
).
It takes advantage of the fact that script blocks serialize as their verbatim source code, excluding {
and }
, so you make the target PowerShell instance invoke it by enclosing it in & { ... }
in the command string.
& { $ScriptBlock }
would work in your particular case, to make the technique robust, any embedded "
characters must be escaped as \"
-escaped in order to ensure that the CLI's initial command-line parsing doesn't remove them.-replace '(?<!\\)(\\*)"', '$1$1\"'
operation below does.Start-Process pwsh @"
-NoProfile -Command & { $($ScriptBlock -replace '(?<!\\)(\\*)"', '$1$1\"') } "$prefix"
"@
Note:
All pass-through arguments for pwsh
are encoded in a single string passed to the (positionally implied) -ArgumentList
parameter.
While you could alternatively make use of the -EncodedCommand
and (currently undocumented) -EncodedArguments
CLI parameters, doing is even more cumbersome, due to requiring Base64 encoding.
If you do want to use these parameters with Start-Process
, see this answer for an example.