Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

$ErrorActionPreference = 'Stop' ignored, when command read from stdin. #21808

Closed
5 tasks done
philcerf opened this issue May 9, 2024 · 15 comments
Closed
5 tasks done
Labels
Resolution-By Design The reported behavior is by design. Resolution-Declined The proposed feature is declined. WG-Interactive-Console the console experience

Comments

@philcerf
Copy link

philcerf commented May 9, 2024

Prerequisites

Steps to reproduce

Hey.

Another issue when reading the commands from stdin, possibly related to #21784.

Consider something like:

$ pwsh -NonInteractive -NoProfile -Command '$ErrorActionPreference = "Stop"; Get-Process -foo; Write-Host BLA'
Get-Process: A parameter cannot be found that matches parameter name 'foo'.

This exits on the error and BLA is not written.

The same when the command is given as argument but with newlines in it:

$ pwsh' -NonInteractive -NoProfile -Command $'$ErrorActionPreference = "Stop"\nGet-Process -foo\nWrite-Host BLA'
Get-Process: 
Line |
   2 |  Get-Process -foo
     |              ~~~~
     | A parameter cannot be found that matches parameter name 'foo'.

Now when read from stdin:

$ pwsh -NonInteractive -NoProfile -Command '-' <<'END'                                                         
$ErrorActionPreference = 'Stop'
Get-Process -foo
Write-Host BLA

END

gives:

Get-Process: A parameter cannot be found that matches parameter name 'foo'.
BLA

However:

$ pwsh -NonInteractive -NoProfile -Command '-' <<'END'                                                        
$ErrorActionPreference = 'Stop'
Get-Process -foo; Write-Host BLA
                                
END

works as expected and does not print BLA:

Get-Process: A parameter cannot be found that matches parameter name 'foo'.

WTF?! ... Reading commands from stdin seems basically completely broken.

Expected behavior

reading commands from stdin should work as if given as `-Command` argument.

Actual behavior

it's completely broken

Error details

No response

Environment data

N/A

Visuals

No response

@philcerf philcerf added the Needs-Triage The issue is new and needs to be triaged by a work group. label May 9, 2024
@rhubarb-geek-nz
Copy link

rhubarb-geek-nz commented May 9, 2024

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.

@jborean93
Copy link
Collaborator

jborean93 commented May 9, 2024

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.

@mklement0
Copy link
Contributor

mklement0 commented May 9, 2024

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 #15331 (comment)

Given the desire to maintain backward compatibility, these behaviors won't change, though even in the context of this behaviors there are two bugs:

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

@philcerf
Copy link
Author

@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.

@SteveL-MSFT
Copy link
Member

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?

@philcerf
Copy link
Author

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.

@mklement0
Copy link
Contributor

mklement0 commented May 10, 2024

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.

@mklement0
Copy link
Contributor

I've created a docs issue:

@SteveL-MSFT SteveL-MSFT added WG-Interactive-Console the console experience WG-NeedsReview Needs a review by the labeled Working Group labels May 13, 2024
@kilasuit
Copy link
Collaborator

kilasuit commented May 15, 2024

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?

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

@theJasonHelmick
Copy link
Collaborator

@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.

@theJasonHelmick theJasonHelmick added Resolution-By Design The reported behavior is by design. Resolution-Declined The proposed feature is declined. and removed Needs-Triage The issue is new and needs to be triaged by a work group. WG-NeedsReview Needs a review by the labeled Working Group labels May 15, 2024
Copy link
Contributor

This issue has been marked as declined and has not had any activity for 1 day. It has been closed for housekeeping purposes.

Copy link
Contributor

This issue has been marked as by-design and has not had any activity for 1 day. It has been closed for housekeeping purposes.

Copy link
Contributor

microsoft-github-policy-service bot commented May 17, 2024

📣 Hey @philcerf, how did we do? We would love to hear your feedback with the link below! 🗣️

🔗 https://aka.ms/PSRepoFeedback

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Resolution-By Design The reported behavior is by design. Resolution-Declined The proposed feature is declined. WG-Interactive-Console the console experience
Projects
None yet
Development

No branches or pull requests

7 participants