Powershell: Cross platform pager using `Microsoft.PowerShell.Pager`

Created on 12 Aug 2020  路  24Comments  路  Source: PowerShell/PowerShell

Summary of the new feature/enhancement

We recently released a cross platform pager as a NuGet package Microsoft.PowerShell.Pager. The intention is to use the Pager for Get-Help and dynamic help.

The has been interest expressed in utilizing the Pager in a more general purpose way.

This issue is to discuss if the Pager should be distributed in some other form. Options on top of my mind in no priority order.

Built-in function or Cmdlet to use as a Pager

Something like, no intention of designing the cmdlet surface here.

Get-Process | Out-Pager 

.NET global tool or Executable

The behavior will be similar to a native pager like less or more.com.

Get-Process | pspager 

Proposed technical implementation details (optional)

Issue-Discussion Issue-Enhancement

Most helpful comment

I wanna throw out that regardless of what route we take here, I want to see the new pager replace the pager used in Out-Host -Paging (which we can't take out). We should only have one pager in the product :)

All 24 comments

Personally, I think an Out-Pager cmdlet would be the most useful. If we can mimic some of the typical options from more or less as parmeters that'd be ideal, of course. IIRC there are some open issues around Out-Host -Paging that we should probably seek to fix for a new native pager implementation like this / ensure they aren't an issue in the new implementation.

A big part of the draw imo is halting the pipeline. Even with how janky Out-Host -Paging is I still use it when I'm looking through results that are expensive to pull all at once. Being able to pause downstream ProcessRecord invocations when the screen fills up is great.

More feedback from @jaykul @vexx32 @jhoneill in issue #13468

I wanna throw out that regardless of what route we take here, I want to see the new pager replace the pager used in Out-Host -Paging (which we can't take out). We should only have one pager in the product :)

We should only have one pager in the product :)

I guess there are users who will prefer Unix less. I hope we preserve the environment variable to pick up a preferred pager.

  1. Traditional user experience in all shells for many years is to explicitly use a pager.

    • So I agreed we shouldn't have -Pager switch in cmdlets.

    • It is true for Out-Host -Pager

    • We have a pager in Help function and we could move the new pager there.

  2. For better discoverability I'd suggest to consider Out-HostPager name
  3. PowerShell could do smart things for paginating but we should investigate such scenarios with caution.

    • Add a flag in Formatting system could annoying users if it is default but this could be opt-in.

    • Traditionally we moderate this by a preference variable/switch/attribute which is possibly superfluous for these scenarios

So my preference - only implement Out-Pager/Out-HostPager in 7.1 and then wait more feedback.

I wanna throw out that regardless of what route we take here, I want to see the new pager replace the pager used in Out-Host -Paging (which we can't take out). We should only have one pager in the product :)

In all my years using PowerShell I don't think I've used Out-host -paging - I learned | more - in about 1983 - and that's too much muscle memory to overturn (same applies to | clip or ping )
Windows PowerShell has more as a function wrapping for more.com and 7 just lets things flow through to the OS's more and a linux user might well use less

Having just tested it , I can't imagine anyone who just wanted paging would | Out-host -paging unless they had never heard of more , writing an error to say you pressed Q... I can, sort of, see that there may some case where you want to know the user didn't look at everything but the muscle memory which says press ctrl+c defeats it.

A big part of the draw imo is halting the pipeline. Even with how janky Out-Host -Paging is I still use it when I'm looking through results that are expensive to pull all at once. Being able to pause downstream ProcessRecord invocations when the screen fills up is great.

Even that is six of one and half a dozen of the other

1..1000 | %{ $_ ; sleep .2} | more

At one screenful you pause for a look, and then hit space for the next screenful and it's buffered and waiting. Downside ? Exiting with Q means waiting for the remaining rows; it needs ctrl + C
1..1000 | %{ $_ ; sleep .2} | out-host -Paging
Puts the brakes on; My hunch is most of the time, most people will want the machine to do the next part in the background while they're reviewing what has been done so far. But some of the people, some of the time will want "Don't do 51-100 until I have reviewed 1-50". I'm trying to think of a case where I'd stop after a screenful not review each item - at least for the first few items.

Having just tested it , I can't imagine anyone who just wanted paging would | Out-host -paging unless they had never heard of more , writing an error to say you pressed Q... I can, sort of, see that there may some case where you want to know the user didn't look at everything but the muscle memory which says press ctrl+c defeats it.

Yeah it's pretty terrible, but that's why we're talking about improving it.

At one screenful you pause for a look, and then hit space for the next screenful and it's buffered and waiting. Downside ? Exiting with Q means waiting for the remaining rows; it needs ctrl + C

Nah it can throw PipelineStoppedException.

My hunch is most of the time, most people will want the machine to do the next part in the background while they're reviewing what has been done so far.

Often retrieving a single page is quite quick while retrieving the entire result set is still expensive. Personally I have the opposite experience, the amount of times I'd have preferred the behavior you described is very rare.

I'm trying to think of a case where I'd stop after a screenful not review each item - at least for the first few items.

Often when exploring something interactively that returns a lot of results, you can tell whether you need to change your search criteria based on the first page. Also if you're fishing for a specific item with broad search criteria, you would want to stop processing the search once you spot what you're looking for.

@PowerShell/powershell-committee discussed this today and advises:

  • This feature needs to be wrapped as an experimental feature as we have open questions about the design. This is needed for 7.1. Everything else below is 7.2+
  • We agree that Out-Pager cmdlet makes sense as Out-Host -Paging is not discoverable
  • We agree that in cases where you have data, then paging should occur in the main screen buffer while deliberate use of Out-Pager (or dynamic help in PSReadLine) should use the alternate screen buffer
  • It may make sense to have paging as a common feature, but did not agree on whether it's a common parameter or some other configuration/preference set by the user

@SteveL-MSFT did the committee also say that Out-Host -Paging should use the new pager?

@SteveL-MSFT

  • We agree that Out-Pager cmdlet makes sense as Out-Host -Paging is not discoverable

It is worth asking the question, why would a user prefer |Out-Pager over |more. ?
To give a parallel Test-NetConnection has not replaced ping for most use cases because (1) We already know ping, as a universal command so the new command needs to _overturn a habit_. (2) most of the time we don't need to process objects returned - we don't achieve _benefit_ from changing, and (3) At 18 characters instead of 4 it doesn't offer _convenience_.
Another parallel is | clip, which I use most days. Windows PowerShell (in V4 or V5) introduced a functionally identical Set-Clipboard which is more typing for something doesn't work everywhere- | clip.exe works in bash on Windows, cmd, and legacy PowerShell (which I still have to work with)

I'm not saying no one will ever prefer it, but before embarking on doing something because it's possible, there needs to be clarity on where it offers a gain (and where it doesn't try to), to justify using effort which could deliver something else. It can't be universal, something already does the job, so in what way is this doing the job better?

  • It may make sense to have paging as a common feature, but did not agree on whether it's a common parameter or some other configuration/preference set by the user

Think very, _very_, very hard about introducing Do-not-return-objects-but-print-text options. Some commands _do_ exist to put stuff onthe console for a human to read (format-table, Get-Help) and their _objects_ are seldom exploited.
But the thinking which says "-Paged would be a good common parameter" would also say "-AsTable , -AsList and -AsStrings would be good parameters" and is countered by the same logic which says no, use format-Table , format-list , or out-string

And again ask the question if / how / when -paged gives a better solution than |more.

@jhoneill the new pager and Out-Host -Paging pager work in a PSReadLine keyhandler. more/less do not. The intention is to use this pager for dynamic help feature in PSRL.

@TylerLeonhardt
PSRL might be sufficient justification for doing a native pager - meaning PS users, generally speaking, not using it doesn't matter much. Improving the pager in out-host makes sense, Wrapping that in a PAGE function (any name <= 4 chars), makes more sense to me than adding a cmdlet to do the same job.

My call to avoid -DoNotReturnObjectsButPrintText common parameters stands though.

@jhoneill the new pager and Out-Host -Paging pager work in a PSReadLine keyhandler. more/less do not.

They work, you just need to pipe to Out-Default:

Set-PSReadLineKeyHandler -Chord 'ctrl+p' -ScriptBlock {
    & { gps | more } | Out-Default
}

Doesn't work over remoting though.

@jhoneill personally I'd just prefer to have a native paging cmdlet so I don't have to bother checking what pager(s) might be available on a given system.

A short alias to it is simple enough if it's a separate cmdlet. Out-Host -Paging already being part of an existing cmdlet tends to mean that adding further options to it will become cumbersome (and discovering them from a function would then either be impossible or require additional unnecessary dev work to manually copy parameters to the function.)

Out-Host -Paging should IMO be a compatibility effort, maintained for existing use and the basic paging functionality, but pagers tend to expose additional options that would be better suited to their own cmdlet than making bloated parameter sets to expand on Out-Host -Paging in my opinion.

@jhoneill personally I'd just prefer to have a native paging cmdlet so I don't have to bother checking what pager(s) might be available on a given system.

I think we might talking at cross-purposes. |more works in pwsh, windows PowerShell, even legacy versions, cmd, bash, whatever. Introducing out-Pager means I first have to stop and think "What pager is available in this shell" and type the longer | Out-Pager. So why wouldn't I do |more ?

A short alias to it is simple enough if it's a separate cmdlet.
At least that removes the need for more typing to get the same effect. I agree on Out-Host -Paging

more only works if that pager is available in the system, from what I can see. It's not provided by PowerShell. Is it usually available? Sure. Always? 馃し

And if you're working in different shells, remembering a slightly different pager command is the _least_ of one's concerns, surely. 馃槀

And even if you forget, as you pointed out, | more would still work in the majority of cases, so you're not losing out there either.

I would say that it is beneficial for PowerShell to treat everything as cmdlets - either as true cmdlet or as a wrapper cmdlet.

Mostly I don't disagree with what's been said above: there are already a few good pagers out there, and they _mostly_ work fine in PowerShell ... except for this one thing ...

An actual PowerShell pager needs to be able to deal with output in alternative streams. Otherwise, why bother?

There are few things more frustrating and confusing than piping output mixed with some verbose/debug/warnings/errors to | more and having each page just those few lines more than a page, thanks to the uncounted alternate stream lines.

(I know I can write it as *>&1 | more but then Warning, Verbose, Debug streams are indistinguishable from the output, it's just a different mess)

Hmm. A pager implementation could be made to format non-output streams according to their typical standard formatting, though, since non-output streams have their own discrete types. That would make it possible to do something like *>&1 | Out-Pager and have the pager still render them recognisably. Whether that should be a default or exposed in a different way... don't know.

more only works if that pager is available in the system, from what I can see. It's not provided by PowerShell. Is it usually available? Sure. Always? 馃し

Every Microsoft operating system (certainly from DOS 3.1, including OS/2, NT3.1 and upwards, and Windows 9x) and every Unix based operating system from when I first used it in 1985, has supported |more and I've never seen it removed from a system. Any computer I've met which uses | supports more.

And if you're working in different shells, remembering a slightly different pager command is the _least_ of one's concerns, surely. 馃槀

It's not the least but one of many small concerns. Don't worry about is test-netconnection, / Get-LocalGroupMember / Set-clipboard valid here when Ping, net localgroup or clip do the job. Just like they did 20 years ago. There are cases where you want the Ping objects, or GroupMember objects, but for clipboard or more, where's the benefit.

And even if you forget, as you pointed out, | more would still work in the majority of cases, so you're not losing out there either.
My point is not _forgetting_. It's non-adoption.

One other point (regarding the -Pager switch in Get-Help): we have "always" had a help function which is just a proxy for Get-Help @PSBoundParameters | more. In my opinion, that's the right way to deliver paging to help -- except that it should respect an environment variable for which pager to use!

We shouldn't be putting custom unique paging functionality into commands, because I want to use the pager which is the best, every time I use a pager (and that might not be your pager, but if you make it handle additional streams, it probably will be).

Hmm. A pager implementation could be made to format non-output streams according to their typical standard formatting, though, since non-output streams have their own discrete types.

This could be handled similar to how Out-Null is currently implemented. During PipelineProcessor init it can check if the downstream command is Out-Pager. If it is then do MergeMyResults(PipelineResultTypes.All, PipelineResultTypes.Output)

Downside is that it might change the order of some things due to the table formatters delay, though the displayed order would be more "correct".

It's not the least but one of many small concerns. Don't worry about is test-netconnection, / Get-LocalGroupMember / Set-clipboard valid here when Ping, net localgroup or clip do the job. Just like they did 20 years ago. There are cases where you want the Ping objects, or GroupMember objects, but for clipboard or more, where's the benefit.

For clip I recently forced myself to start using scb because of extra new line characters and encoding issues. ping is sort of a loaded scenario because it Test-Connection was pretty wonk for some time (ty @vexx32 for fixing it).

Also all the talk about how native more/less variants are better but none of those work over remoting still right? It's still just Out-Host -Paging that works?

Was this page helpful?
0 / 5 - 0 ratings