-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Description
Steps to reproduce
Run
Get-ChildItem -Recurse -Force | morethen exit more by pressing q.
Expected behavior
The pipe stops after quitting more.
Actual behavior
The pipe keeps on running until all child items listed.
Environment data
PSVersion 7.1.3
PSEdition Core
Description
There are a couple of issues related to unix-like plaforms and pipe handling (#8420 #8421) but the same behavior seems to appears on any platform with any type of external program.
Is this a known/documented issue? I looked around but maybe my search terms are not ok, I couldn't find a related issue.
For example: these keep running the upstream command even after the downstream command closes; (the cmd /c '@echo off' command is just to mimick an external program which consumes the pipe then stops without output before all items are consumed - a real example would be a program like fzf):
ls -Rec -Fo -File | more
ls -Rec -Fo -File | cmd /c '@echo off'
this can be worked around with Select-Object to force a StopUpstreamCommandsException, e.g. this works as expected in that the pipe stops:
ls -Rec -Fo -File | & "C:\Program Files\Git\usr\bin\head.exe" | Select-Object -First 10
however that's only ok because it is known head will by default produce 10 items. That's a problem because it means piping into a command which produces no output will not stop the pipe when that command exits. So this still keeps on running:
ls -Rec -Fo -File | cmd /c '@echo off' | Select-Object -First 1 | Out-Null
One workaround is be to launch the external program with the .net Process API, then use Register-ObjectEvent -EventName 'Exited' and then throw a StopUpstreamCommandsException using a technique like https://stackoverflow.com/a/34800670. But that's non-trivial.
Another simpler one would be to make the external command always produce output no matter what. I hoped pipeline chain operators could do that but due to associativity that doesn't cut it. That is to say, I assume that
ls -Rec -Fo -File | cmd /c '@echo off' || 'TERMINATOR' | Select-Object -First 1 | Out-Null
doesn't stop right away because it comes down to
(ls -Rec -Fo -File | cmd /c '@echo off') || 'TERMINATOR' | Select-Object -First 1 | Out-Null
and not
ls -Rec -Fo -File | (cmd /c '@echo off' || 'TERMINATOR') | Select-Object -First 1 | Out-Null
(which yields a ParserError).
The idea to always have the external command produce output can be done in cmd like
ls -Rec -Fo -File | cmd /c '(@echo off || echo TERMINATOR) && echo TERMINATOR' | Select-Object -First 1 | Out-Null
i.e. no matter if the external program (as said mimicked by @echo off) produces output or not, the echos after it make sure it does. Is there another workaround?