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

Select-Object -First X skips End{ } blocks of previous cmdlets #7930

Closed
vexx32 opened this issue Oct 3, 2018 · 10 comments
Closed

Select-Object -First X skips End{ } blocks of previous cmdlets #7930

vexx32 opened this issue Oct 3, 2018 · 10 comments
Labels
Issue-Discussion the issue may not have a clear classification yet. The issue may generate an RFC or may be reclassif Resolution-No Activity Issue has had no activity for 6 months or more WG-Engine core PowerShell engine, interpreter, and runtime

Comments

@vexx32
Copy link
Collaborator

vexx32 commented Oct 3, 2018

Using the -First $n parameter for Select-Object completely skips the end {} blocks of all cmdlets preceding it in the pipeline.

This can be quite hazardous, e.g., for a cmdlet that access a resource that needs to have that handle properly disposed of (like a file) may not release that handle correctly.

There is no finally {} block available to PS, and the method in which Select-Object terminates the pipeline appears to happen in such a manner that no script-based function can account for; that is, even with a try...finally construct in the cmdlets' process {} blocks it will not kick off in the event that Select-Object is used to prematurely end the pipeline.

Instead, a method should be exposed for Select-Object (and potentially other cmdlets?) to safely terminate the pipeline by ceasing all process steps and kicking off the chain of end {} steps in the pipeline.

Steps to reproduce

(shamelessly pinched from @TimCurwick's fantastic example he posted in the PS Slack)

function test {
    [cmdletbinding()]
    param(
        [Parameter(ValueFromPipeline)]
        $X 
    )
    begin {
        Write-Host "Begin test"
    }
    process {
        Write-Host "Process test"
        $X
    }
    end {
        Write-Host "End test"
    }
}
    
1, 2, 3 | test | test | select -first 2

Expected behavior

Begin Test
Begin Test
Process Test
Process Test
1
Process Test
Process Test
2
End Test
End Test

Actual behavior

Begin test
Begin test
Process test
Process test
1
Process test
Process test
2

Environment data

> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      6.1.0
PSEdition                      Core
GitCommitId                    6.1.0
OS                             Microsoft Windows 10.0.17763
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0
@BrucePay
Copy link
Collaborator

BrucePay commented Oct 3, 2018

Hi @vexx32 end is not intended to be a cleanup block. Cleanups in cmdlets should be handled in the Dispose() implementation. For scripts, there currently isn't an equivalent solution. Issue #6673 was opened to address this.

@BrucePay BrucePay added WG-Engine core PowerShell engine, interpreter, and runtime Issue-Discussion the issue may not have a clear classification yet. The issue may generate an RFC or may be reclassif labels Oct 3, 2018
@vexx32
Copy link
Collaborator Author

vexx32 commented Oct 3, 2018

Certainly, @BrucePay but given that output from end{} is valid and an accepted pattern, there exist many cases with Select-Object -First $n where that output (which may be perfectly valid regardless of the decision to accept only the first $n items from process {} may not occur.

There may also be logging steps or any number of actions that one would expect to be performed in the end {} step which is, silently and without any form of documentation explaining the behaviour, completely skipped without warning. There is one line in the Select-Object documentation that indicates that it stops prior commands. This is, however, without mention that potentially significant portions of all previous commands in the entire pipeline sequence preceding Select-Object will be summarily skipped. In a sense, it feels a bit like a breach of the pipeline contract, so to speak, if there was such a thing.

The worst part is, because this is implemented in this fashion, there is absolutely no way for a module author to combat this. Select-Object -First $n is an effective way of completely neutering any cmdlet's end {} block, whether that is your intention or not.

Appreciate the linked issue, but I think that given the pipeline standard of executing begin {} once, process {} for each object, and end {} once, it simply doesn't make sense for a cmdlet from another module to entirely and without any kind of warning for the user skip entire segments of other cmdlets' or functions' code.

@BrucePay
Copy link
Collaborator

BrucePay commented Oct 3, 2018

@vexx32

but given that output from end{} is valid and an accepted pattern, there exist many cases with Select-Object -First $n where that output (which may be perfectly valid regardless of the decision to accept only the first $n items from process {} may not occur

-first n stops after n characters have been written. That's the literal definition of -first. It doesn't matter if those characters come from process or end, you ask for n characters, you get n characters. Very simple. And there has never been any contract that says all pieces of the pipeline will execute fully. Any cmdlet can throw a pipeline-terminating exception, or just a terminating exception or the user can hit ctrl-c. Things are guaranteed be called in begin,process,end order but not necessarily that they will all be called.

@vexx32
Copy link
Collaborator Author

vexx32 commented Oct 3, 2018

Sure, in cases of error, not everything happens. But Select-Object abusing that means that any sane user who utilises that (awfully handy) functionality remains unaware that code has been skipped -- until something else goes wrong further down the line.

In cases of error, this behaviour is relatively obvious. In Select-Object's usage? It's not an error state, it's a deliberately induced state that has unforeseeable consequences unless one is familiar with the underlying code, and shouldn't directly alter other functions' code paths like this.

@mklement0
Copy link
Contributor

mklement0 commented Oct 3, 2018

I agree that from an end user's perspective there's a crucial difference between something throwing in a pipeline - an unexpected error that, if unhandled, aborts processing - and using Select-Object -First to only process part of the pipeline input - a regular feature during normal operation that simplify modifies processing.

It just so happens that the way Select-Object -First stops the upstream cmdlets is also implemented as an exception behind the scenes, but that is an implementation detail that shouldn't determine the behavior, for the reasons @vexx32 has already stated.

@vexx32
Copy link
Collaborator Author

vexx32 commented Nov 6, 2020

This issue would be alleviated somewhat with the introduction of cleanup{} in this PR: #9900

Copy link
Contributor

This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

2 similar comments
Copy link
Contributor

This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

Copy link
Contributor

This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

@microsoft-github-policy-service microsoft-github-policy-service bot added Resolution-No Activity Issue has had no activity for 6 months or more labels Nov 16, 2023
Copy link
Contributor

This issue has been marked as "No Activity" as there has been no activity for 6 months. It has been closed for housekeeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Issue-Discussion the issue may not have a clear classification yet. The issue may generate an RFC or may be reclassif Resolution-No Activity Issue has had no activity for 6 months or more WG-Engine core PowerShell engine, interpreter, and runtime
Projects
None yet
Development

No branches or pull requests

3 participants