Powershell: Add HistoryInfo.Duration to default display for HistoryInfo

Created on 2 May 2019  路  11Comments  路  Source: PowerShell/PowerShell

Summary of the new feature/enhancement

When I run Get-History, I get this:

  Id CommandLine
  -- -----------
   1 Add-Type -Path C:\Temp\Humanizer.Core.2.6.2\lib\netstandard2.0\Humanizer.dll
   2 $ts = [TimeSpan]::new(1,42,69)
   3 [Humanizer.TimeSpanHumanizeExtensions]::Humanize($ts, 3)
   4 start-sleep 12

I want to get this:

Id   Duration CommandLine
--   -------- -----------
 1 0.00:00:00 Add-Type -Path C:\Temp\Humanizer.Core.2.6.2\lib\netstandard2.0\Humanizer.dll
 2 0.00:00:00 $ts = [TimeSpan]::new(1,42,69)
 3 0.00:00:00 [Humanizer.TimeSpanHumanizeExtensions]::Humanize($ts, 3)
 4 0.00:00:12 start-sleep 12

It is great that we added Duration but this info is a bit buried for the average user. We could surface Duration in the default output of Get-History. It would be easy. The question is, is this a good "add"? I think so but wonder what other folks think.

Proposed technical implementation details (optional)

Update the format ps1xml file for HistoryInfo to add a table column (right aligned) for Duration using a formatting string like "d\.hh\:mm\:ss". We would need to check the implications of different culture info.

Issue-Enhancement Resolution-Fixed

Most helpful comment

I'm thinking about adding this to the format data for HistoryInfo in a PR. Thoughts?

            <ScriptBlock>
                if ($_.Duration.TotalDays -ge 1) {
                    $formatString = "d\.hh\:mm\:ss\.fff"
                }
                elseif ($_.Duration.TotalHours -ge 1) {
                    $formatString = "h\:mm\:ss\.fff"
                }
                elseif ($_.Duration.TotalMinutes -ge 1) {
                    $formatString = "m\:ss\.fff"
                } 
                else {
                    $formatString = "s\.fff"
                }

                $_.Duration.ToString($formatString)
            </ScriptBlock>

This gives output like this:

  Id        Duration CommandLine
  --        -------- -----------
  63           0.017 Update-FormatData -PrependPath .\Documents\WindowsPowerShell\format.ps1xml
  64           0.134 ghy
  65          42.014 start-sleep 42
  66           0.132 ghy
  67           4.007 start-sleep 4
  68        1:05.008 start-sleep 65
  69           0.198 ghy
  70           0.010 "1.00:00:00.000".length
  71           0.018 Update-FormatData -PrependPath .\Documents\WindowsPowerShell\format.ps1xml
  72           0.145 ghy

The cell width is set to 15 which would leave room for a timespan for up to 9 days (d.hh:mm:ss.fff). Perhaps, we only want a cell width that supports up to single digit hours? That would reduce the cell width by 3. Not sure it is worth it but this is what a cell width of 12 looks like:

  Id     Duration CommandLine
  --     -------- -----------
  65       42.014 start-sleep 42
  66        0.132 ghy
  67        4.007 start-sleep 4
  68     1:05.008 start-sleep 65
  69        0.198 ghy
  70        0.010 "1.00:00:00.000".length
  71        0.018 Update-FormatData -PrependPath .\Documents\WindowsPowerShell\format.ps1xml
  72        0.145 ghy
  73        0.043 ghy | select -Last 10
  74        0.019 Update-FormatData -PrependPath .\Documents\WindowsPowerShell\format.ps1xml

All 11 comments

Formatting changes are not considered breaking and I can see how this can be useful. My only concern is the fixed formatting particularly when most commands take ms, some take secs, and a few take mins, but most don't take hours or days. Perhaps a humanizer approach?

Not sure why we'd use a custom format string there tbh. Doesn't .NET already have predefined format strings for Timestamp (or at least DateTime, which we can borrow) values?

There's no built-in formatter for Timespan which is why you get this display (blech):

PS> ghy | % duration

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 4
Milliseconds      : 2
Ticks             : 40022248
TotalDays         : 4.63220462962963E-05
TotalHours        : 0.00111172911111111
TotalMinutes      : 0.0667037466666667
TotalSeconds      : 4.0022248
TotalMilliseconds : 4002.2248

RE length, perhaps we only display hh:mm:ss since day spanning execution is probably pretty rare.

Id Duration CommandLine
-- -------- -----------
 1 00:00:04 start-sleep 4
 2 00:00:00 ghyy
 3 00:00:00 ghy
 4 00:00:00 ghy | fl
 5 00:00:00 ghy | % duration
 6 00:00:00 ghy | ft Id, @{n="Duration";e={$_.Duration.ToString("hh\:mm\:ss")};a="right"}, Command

I messed around a bit with Humanizer and it turns out "4 seconds" is one char wider than the duration output above.

@rkeithhill just to be clear, I wasn't suggesting literally using Humanizer, but the concept. Also, I think it needs to start with milliseconds and not seconds:

Id Duration CommandLine
-- -------- -----------
 1       4s start-sleep 4
 2    250ms ghyy
 3     4.3s ghy
 4   1m 10s ghy | fl
 5   2h 35m ghy | % duration

That's definitely more readable on a per-command basis, but it makes comparing different entries in history much less clear from a visual standpoint. 馃槙

@vexx32 that's a fair point. Since it's formatting, we can go with one solution and get feedback. Minimally, the smallest unit has to be milliseconds though.

I'd probably think we need something along the lines of...

Id Duration     CommandLine
-- ------------ -----------
 1 00:00:04.000 start-sleep 4
 2 00:00:00.250 ghyy
 3 00:00:04.300 ghy
 4 00:01:10.000 ghy | fl
 5 02:35:00.000 ghy | % duration

That does start to look a little long and unclear, unfortunately... but if we want to allow for those levels of granularity it might end up being the most sensible.

We could optionally just leave off the hour portion for those commands that don't get up to that mark, leaving us with something like this:

Id Duration     CommandLine
-- ------------ -----------
 1    00:04.000 start-sleep 4
 2    00:00.250 ghyy
 3    00:04.300 ghy
 4    01:10.000 ghy | fl
 5 02:35:00.000 ghy | % duration

Or if we want to have it be essentially similar to your previous suggestion @SteveL-MSFT, we could opt to do just perhaps only up to the significant digits (wording is hard), but leaving it all right-aligned in a way that lets you tell at a glance which is quicker:

Id Duration     CommandLine
-- ------------ -----------
 1        4.000 start-sleep 4
 2        0.250 ghyy
 3        4.300 ghy
 4     1:10.000 ghy | fl
 5 02:35:00.000 ghy | % duration

Nice, here is a related article by Tommy. It's awesome.
https://powershell.org/2019/04/get-history-modified/

If you shorten it to significant digits, you have to put units on it.
To people who see this for the first time, it's not obvious what's going on without the units.

I mean, looking at mine right this minute, I have 56 items in history and only one of them took "1m 20.289s" -- the rest all took less than 20s.

FWIW, I use something like this in my format file, to maximize the accuracy when things are fast, and leave off the ms when things are slow ...

$Duration = $_.EndExecutionTime - $_.StartExecutionTime
if ($ts.Minutes) {
    if ($ts.Hours) {
        if ($ts.Days) {
            return "{0:##}d {1:00}h {2:00}m" -f $ts.Days, $ts.Hours, $ts.Minutes
        }
        return "{0:##}h {1:00}m {2:00}s" -f $ts.Hours, $ts.Minutes, $ts.Seconds
    }
    return "{0:##}m {1:n3}s" -f $ts.Minutes, ($ts.TotalSeconds - ($ts.Minutes * 60))
}
return "{0:n7}s" -f $ts.TotalSeconds # could be 5, or 3 ...

I'm thinking about adding this to the format data for HistoryInfo in a PR. Thoughts?

            <ScriptBlock>
                if ($_.Duration.TotalDays -ge 1) {
                    $formatString = "d\.hh\:mm\:ss\.fff"
                }
                elseif ($_.Duration.TotalHours -ge 1) {
                    $formatString = "h\:mm\:ss\.fff"
                }
                elseif ($_.Duration.TotalMinutes -ge 1) {
                    $formatString = "m\:ss\.fff"
                } 
                else {
                    $formatString = "s\.fff"
                }

                $_.Duration.ToString($formatString)
            </ScriptBlock>

This gives output like this:

  Id        Duration CommandLine
  --        -------- -----------
  63           0.017 Update-FormatData -PrependPath .\Documents\WindowsPowerShell\format.ps1xml
  64           0.134 ghy
  65          42.014 start-sleep 42
  66           0.132 ghy
  67           4.007 start-sleep 4
  68        1:05.008 start-sleep 65
  69           0.198 ghy
  70           0.010 "1.00:00:00.000".length
  71           0.018 Update-FormatData -PrependPath .\Documents\WindowsPowerShell\format.ps1xml
  72           0.145 ghy

The cell width is set to 15 which would leave room for a timespan for up to 9 days (d.hh:mm:ss.fff). Perhaps, we only want a cell width that supports up to single digit hours? That would reduce the cell width by 3. Not sure it is worth it but this is what a cell width of 12 looks like:

  Id     Duration CommandLine
  --     -------- -----------
  65       42.014 start-sleep 42
  66        0.132 ghy
  67        4.007 start-sleep 4
  68     1:05.008 start-sleep 65
  69        0.198 ghy
  70        0.010 "1.00:00:00.000".length
  71        0.018 Update-FormatData -PrependPath .\Documents\WindowsPowerShell\format.ps1xml
  72        0.145 ghy
  73        0.043 ghy | select -Last 10
  74        0.019 Update-FormatData -PrependPath .\Documents\WindowsPowerShell\format.ps1xml

:tada:This issue was addressed in #9751, which has now been successfully released as v7.0.0-preview.2.:tada:

Handy links:

Was this page helpful?
0 / 5 - 0 ratings