When playing with the -Command -
option of the PowerShell executable, I came across the following limitation:
Write-Host "SETUP"
Set-PSDebug -Trace 2
Write-Host "TEST ONE"
$foo = "BAR"
Write-Host "TEST TWO"
Get-ChildItem
Write-Host "TEST THREE"
$bar = @"
BAZ
"@
Write-Host "DONE"
If I save the previous content to test.ps1
and execute cat .\test.ps1 | powershell -Command -
, I get the following output:
SETUP
DEBUG: 1+ >>>> Write-Host "TEST ONE"
DEBUG: ! CALL function '<ScriptBlock>'
TEST ONE
DEBUG: 1+ >>>> $foo = "BAR"
DEBUG: ! CALL function '<ScriptBlock>'
DEBUG: ! SET $foo = 'BAR'.
DEBUG: 1+ >>>> Write-Host "TEST TWO"
DEBUG: ! CALL function '<ScriptBlock>'
TEST TWO
DEBUG: 1+ >>>> Get-ChildItem
DEBUG: ! CALL function '<ScriptBlock>'
Directory: C:\empty
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 26/02/2017 15:39 179 test.ps1
DEBUG: 1+ >>>> Write-Host "TEST THREE"
DEBUG: ! CALL function '<ScriptBlock>'
TEST THREE
As you can see, the output from the Write-Host "DONE"
line never shows.
So, there seems to be something going on for here strings when evaluating line by line. Why is this happening?
And, more importantly, is there a way to make here strings work in this context?
Note: The following applies as of Windows PowerShell v5.1.14393.693 and PowerShell Core v6.0.0-alpha16.
PetSerAl, as he often does, provides the crucial pointer in a comment on the question (and later, via comments, also provided the simplest fix):
It seems that the problem is not with here-strings [per se], but with multiline commands [in general].
-Command -
requires at least 1 empty line after the last multi-line command in the input for all commands to be recognized properly.
A here-string is by definition an instance of a multi-line command, which is why you've experienced the problem.
Therefore, to work around the problem in scripts that contain multi-line commands, insert a single empty line at the end:
Write-Host "SETUP"
Set-PSDebug -Trace 2
Write-Host "TEST ONE"
$foo = "BAR"
Write-Host "TEST TWO"
Get-ChildItem
Write-Host "TEST THREE"
$bar = @"
BAZ
"@
Write-Host "DONE" # IMPORTANT: Note the empty line below.
The answer can be found in powershell's own CLI help (powershell -?
), whose description of the -Command
parameter starts with (emphasis added):
Executes the specified commands (and any parameters) as though they were typed at the Windows PowerShell command prompt
Without the PSReadLine
module loaded, when you type a multi-line command interactively, you need an extra Enter keystroke to submit the entry, so as to let PowerShell know when you're done entering the command.
Note: The PSReadLine
module, which became a standard part of interactive PowerShell sessions in W10/PSv5, thankfully obviates the need for this extra keystroke, because it infers from the syntax of what's been entered whether the command is complete.
However, PSReadLine
is not involved when you pass commands via -Command -
.
If you want to experience how PowerShell itself parses multi-line commands in an interactive session, execute Remove-Module PSReadLine
first.
Unfortunately, the same logic is applied when -Command -
is used to provide the commands via stdin, so you must simulate the extra Enter keystroke with an empty line in your input, although PetSerAl figured out that a single empty line at the very end is enough.
If you'd like to see this behavior changed and/or want proper support for -File -
to treat the input like a script, consider making your voice heard in GitHub issue #3223.