I am trying to start a Powershell window that starts an ssh session with the following command:
pwsh.exe -noexit -Command {ssh <username>@<host>}
This works when executed from a pwsh.exe window itself:
but when executed from cmd, the run window (Win+R) or Task Scheduler (what I need it for), it just displays the command that should have been executed and starts pwsh:
That is, the command that doesn't work outside of PowerShell is:
pwsh.exe -noexit -Command {ssh username@host}
The commands have of course been tested with actual websites where, again, 1 works and 2 doesn't.
Any help would be greatly appreciated! Thank you!
Note:
The answer below applies to the CLIs of both PowerShell editions, (powershell.exe
for Windows PowerShell, pwsh
for PowerShell (Core) 7+)
powershell -Command "& { ... }"
/ pwsh -Command "& { ... }"
is an anti-pattern, and therefore to be avoided:
Use "..."
, not "& { ... }"
, i.e. specify all commands directly,[1] which avoids unnecessary syntactic ceremony and unnecessary processing (though the latter won't matter in practice).
This anti-pattern, which is unfortunately very common, presumably arose because older versions of the CLI documentation erroneously suggested that & { ... }
is required, but this has since been corrected.
tl;dr
From inside PowerShell, use pwsh { ... }
(-Command
implied); in your case:
pwsh.exe -noexit { ssh username@host }
ssh username@host
directly.From outside PowerShell, use pwsh -Command " ... "
(or pwsh -c " ..."
); in your case (as in your own answer):
pwsh.exe -noexit "ssh username@host"
Outside can mean:
cmd.exe
"
chars. that are escaped as \"
- see this answer for robust workarounds.Run
dialog (WinKey+R) and commands launched by Task Scheduler."..."
), but if you're calling from a Unix (POSIX-compatible) shell such as Bash, single-quoting is also an option.For a comprehensive overview of the PowerShell CLI, see this post.
Background information:
{ ... }
is the literal form of a PowerShell script block, loosely speaking a reusable piece of code that must be invoked on demand, either with &
(in a child scope) or with .
(directly in the caller's scope):
Only inside PowerShell can it be used with the PowerShell CLI to pass commands to execute, because PowerShell there offers { ... }
as syntactic sugar:
-EncodedCommand
; similarly, any arguments passed to -Args
are Base64-encoded and passed via -EncodedArguments
, and CLIXML (PowerShell's XML-based inter-process serialization format) is requested as the output format via -OutputFormat XML
When the PowerShell CLI is called from the outside (or even via string from inside PowerShell), the syntactic sugar does not apply, and a script-block literal (invariably provided via a string argument) is parsed as just that: a piece of code to be called later.
Thus, if you try something like the following:
# !! WRONG - simply *prints* what's between { and }
# (Also applies if you don't use "..." from outside PowerShell.)
pwsh -c "{ Get-Date }"
{
and }
. Get-Date
is printed, which you can also verify as follows from inside PowerShell: { Get-Date }.ToString()
[1] Technically, you could use "& { ... } ..."
if you wanted to pass arguments from inside your command string to the embedded script block, though that level of encapsulation will rarely be necessary in practice; a contrived example (call from outside PowerShell):
pwsh -c "& { 'Time is now: ' + $args[0].TimeOfDay } (Get-Date)"