Closed philcerf closed 2 weeks ago
See also:
There is a consistency rather than being completely broken.
Consider
pwsh -NonInteractive -NoProfile -Command '-' <<'END'
$ErrorActionPreference = 'Stop' ; Get-Process -foo; Write-Output foo
Get-Process -foo; Write-Output bar
Write-Host BLA
$ErrorActionPreference
END
Results in
Get-Process: A parameter cannot be found that matches parameter name 'foo'.
Get-Process: A parameter cannot be found that matches parameter name 'foo'.
BLA
Stop
My explanations often start with PowerShell is not a UNIX shell. UNIX programs are pipe/stream based, PowerShell is record based.
Now consider each line in stdin as its own record and executed one after the other, however in the same session so the state accumulates, so setting $ErrorActionPreference
is persistent in the session.
So notice that the Write-Output 'foo'
and Write-Output 'bar'
were never executed because within that line the Get-Process caused a terminating error.
The stdin reader acts like the command line repl so things like $ErrorActionPreference = 'Stop'
does apply but the next line is read in stdin so will still run. It's just like copy/pasting the stdin data to the actual console.
I find when you want to use -Command -
with stdin input you are best to wrap the code to run in a scriptblock so that the standard rules apply to a file to run.
pwsh -NoProfile -NoLogo -Command - << EOF
& {
code here
}
EOF
You still need the trailing newlines to ensure PowerShell sees the end of the }
and runs it but everything inside the {}
runs as one statement so things like $ErrorActionPreference = 'Stop'
applies.
Indeed, as discussed in the previously mentioned #3223 and in #15331, both -Command -
and -File -
were seemingly designed to exhibit REPL-like behavior, were the code being provided via stdin isn't run as a whole - as it would be with a direct -Command
argument or with a file argument passed to -File
- but statement by statement.
-File -
is especially baffling, since it prints the prompt string and the statement about to be executed before each statement, but there's seemingly code out there that relies on this - see https://github.com/PowerShell/PowerShell/issues/15331#issuecomment-830451818
Given the desire to maintain backward compatibility, these behaviors won't change, though even in the context of this behaviors there are two bugs:
An incomplete statement is ignored, as mentioned in https://github.com/PowerShell/PowerShell/issues/15331#issuecomment-830816330; at the time, no separate issue was filed, but I now see that you have since done so in #21644.
A parser error occurring in the last statement doesn't result in a nonzero exit code: you've reported this in #21784.
Note that when -Command -
is truly used to create a REPL, the exit code isn't all that interesting, as it will only tell you the last statement's success status, whereas the point of a REPL is to keep the process open and feed multiple, separate statements to PowerShell, whose success status would have to be determined differently.
However, for non-REPL, all-statements-at-once use - which requires the workaround demonstrated by @jborean93 - a parser error should still result in an exit code of 1
.
In other words: the following should print 1
after the parser error message, whereas it currently reports 0
# From Bash
pwsh -NoProfile -NoLogo -Command - <<'EOF'
& {
1 -eqFOO 1
}
EOF
echo $?
Taking a step back:
Getting proper script-via-stdin CLI behavior would require introducing either a dedicated new CLI parameter, say a -CommandFromStdin
, or some magic name other than -
to request this behavior by -Command
/ -File
argument.
Alternatively, there's the option to piggyback on the still-experimental PSCommandWithArgs
feature, which introduces a -CommandWithArgs
CLI parameter that supports passing arguments to the piece of code passed as a single string to this parameter. Since there's no backward-compatibility burden there, ... | pwsh -CommandWithArgs -
could be made to exhibit the expected script-via-stdin CLI behavior
@rhubarb-geek-nz
There is a consistency rather than being completely broken.
My explanations often start with PowerShell is not a UNIX shell. UNIX programs are pipe/stream based, PowerShell is record based. Now consider each line in stdin as its own record and executed one after the other, however in the same session so the state accumulates, so setting
$ErrorActionPreference
is persistent in the session.
Which would be fine, if PowerShell really always did it like that, but it doesn't. If I execute the whole thing as one string (with newlines) passed as single argument to -Command
it works as expected. As it probably also does when executing it as .ps1
file.
IMO it simply never makes sense to have the exactly same script behave completely different depending on how it's read.
Now as @jborean93 and @mklement0 mentioned, the stdin reader apparently behaves like some semi-interactive prompt.
But again, this seems completely strange to me, especially when invoked with -NonInteractive
.
Plus it kinda defeat's the whole purpose of reading from stdin.
As you might have guessed, I come from the Linux world, where a program in such a case would check whether stdin is connected to a terminal or not.
No idea whether windows has such a concept, but if one says -File -
or -Command -
one most like does not want something like an interactive prompt, cause then one would have simply omitted the options altogether?
Even if it's decided that this is in fact the desired behaviour, there should IMO be quite some warnings about it in the help texts to -Command -
and -File -
cause right now those read as if they'd behave just as if instead of -
a real script respectively script file would be given (which again would behave as expected).
Taking a step back:
Well, in the end it's your decision, and if there'd be some warning in the description of these options which tells about the behaviour I'd also consider the issue as "solved", but...
From a perspective of what's common sense and assuming that PowerShell will be a long time around, I would suggest to rather do the compatibility break[0] now than live one with something that is in itself inconsistent[1].
[0] And do you really think that a lot people actually depend on that behaviour? I mean it's even not documented as it actually behaves, so anyone depending on this would already use it by accident. Plus what sense would it make to use -Commnd -
and want the script to be semi-interactively read, when one can just omit the option for the same?
[1] Cause you'd then have -Command someReadCommandsHere
which behaves non-interactively, -File someRealFile
which behaves non-interactively, -CommandFromStdin
which behaves non-interactively, but -Command -
and -File -
which suddenly behave semi-interactively. And at the same time, as you've said, for -CommandWithArgs
(which I guess is modelled after POSIX behaviour - i.e. a script file + args) it would probably make quite some sense to really change the behaviour - so yet another unexpected deviation from homogenous behaviour.
I hear you - #3223 pretty much argued the same points.
I have no say in what technically breaking changes are considered acceptable; my previous comment links to at least one instance of existing code that would break.
🤞that at least -CommandWithArgs
will be modified to work sensibly.
I think it's more than a bucket 3 breaking change to change the -
behavior. However, if the desire is to be able to pipe a script to pwsh
via STDIN, then wouldn't it be better to introduce a new parameter strictly for that purpose?
As for amending the docs
I mostly meant what comes out of -help
and friends.
As for -
, one further way of handling that would be, to completely remove it for -Command
and -File
… and instead provide completely new options for both use cases (non-interactive vs semi-interactive).
Perhaps should there ever be a new major version.
That way, any current user would fail fast and could quickly adapt their code, without any silent issues.
The benefit would be, that you'd get rid of the ambiguously different behaviour within a single option.
One could further argue, that at least for -File -
it should have been even more clear to anyone that the behaviour should have been from the beginning like as if it would be a (script) file, i.e. non-interactive... and that the current behaviour i simply a bug and not something where one could rely upon for backwards compatibility.
Anyway... I'm now simply using multi-line strings (instead of here-documents) to pass a script to PowerShell via -Command
and no longer via stdin... so for me it doesn’t really matter much.
I mostly meant what comes out of
-help
and friends.
Command-specific help topics as well as conceptual help topics including the one for the CLI, about_Pwsh, are maintained both online and - assuming you've run Update-Help
- locally.
However, a duplicate of the content of about_Pwsh is also baked into the pwsh
binary (for invocation via pwsh -help
, pwsh --help
, pwsh -?
, pwsh /?
), which therefore requires a source-code change - but I'd still start with creating an issue in the docs repo - they'll know what to do.
@SteveL-MSFT, personally I think that fixing the problem in the context of -CommandWithArgs
is sufficient and preferable, but I wouldn't complain about a separate, new parameter either.
As for what could be done if backward compatibility were NOT a concern (a collection of proposed breaking changes can be found in #6745, but there are no plans for a version allowed to make such fundamentally breaking changes):
Introduce a new parameter, say, -REPLMode
to request the REPL behavior (modeled on the -SSHServerMode
parameter, possibly with an additional switch distinguishing between the two REPL modes provided by -Command -
vs. -File -
, if needed).
Make -File -
behave as known from POSIX-compatible shells: read the entire stdin input, and treat it as a whole as the content of an (anonymous) script - along with support for pass-through arguments (which aren't currently supported with -File -
); with stdin input only, -File -
should be implied (as is currently already the case);
There is then no need to also support -Command -
; it would amount to unnecessary duplication of functionality.
I've created a docs issue:
I think it's more than a bucket 3 breaking change to change the
-
behavior. However, if the desire is to be able to pipe a script topwsh
via STDIN, then wouldn't it be better to introduce a new parameter strictly for that purpose?
I agree that adding a new parameter makes more sense than changing behaviour as can then be better documented than this current mix of semi-broken expectations vs how PowerShell currently works which gives this mixed outputs.
Interactive-UX WG (which I can't make) will be discussing this later tonight so expect a response from one of the rest of the WG on this
@philcerf - Thank you for this issue. The existing -command and -file were not intended for this scenario. The WG is not certain that the new scenario is needed since other ways are available to achieve this behavior. We would need to see more compelling use cases to consider adding a feature for this use case. We will update the documentation (Thank you @mklement0 ) to explain the current behavior.
This issue has been marked as declined and has not had any activity for 1 day. It has been closed for housekeeping purposes.
This issue has been marked as by-design and has not had any activity for 1 day. It has been closed for housekeeping purposes.
📣 Hey @philcerf, how did we do? We would love to hear your feedback with the link below! 🗣️
🔗 https://aka.ms/PSRepoFeedback
Prerequisites
Steps to reproduce
Hey.
Another issue when reading the commands from stdin, possibly related to #21784.
Consider something like:
This exits on the error and
BLA
is not written.The same when the command is given as argument but with newlines in it:
Now when read from stdin:
gives:
However:
works as expected and does not print
BLA
:WTF?! ... Reading commands from stdin seems basically completely broken.
Expected behavior
Actual behavior
Error details
No response
Environment data
Visuals
No response