A Quick History Lesson
Today, I did a significant amount of work in a PowerShell session and, even though most of what I did was copy/pasted from a file (open in VS Code), I wanted to grab only the essential commands from the session.
Instead of scrolling or arrowing up (which is actually using PowerShell history, by default), you can list all of the commands that you have executed in your session, up to a predefined maximum.
Note: All of the output is from PowerShell 7.0.0-preview.6.
Preview 6 dropped last week on 11/21/2019, as announced by Steve Lee. If you are a fan of
PowerShell and you don’t know Steve, you have some homework to do.
Historical Facts
As with most commands, you can find more about it through PowerShell’s powerful help system.
Conceptual Help
Most PowerShell concepts have an “about_*” help file that could be considered a primer for the topic.
Get-Help about_History
ABOUT HISTORY
Short Description
Describes how to get and run commands in the command history.
Long Description
When you enter a command at the command prompt, PowerShell saves the
command in the command history. You can use the commands in the history as
a record of your work. And, you can recall and run the commands from the
command history.
History Cmdlets
PowerShell has a set of cmdlets that manage the command history.
Cmdlet Alias Description
---------------- ------- --------------------------------------------
Get-History h Gets the command history.
Invoke-History r Runs a command in the command history.
Add-History Adds a command to the command history.
Clear-History clhy Deletes commands from the command history.
<TRUNCATED>
Cmdlet Help
Next, let’s examine the cmdlets.
Get-Help Get-History -ShowWindow
You should skim through the help for the rest of the cmdlets to familiarize yourself with what they do. I will provide
examples for Get-History
and Invoke-History
further down the page.
Preference Variable
As we learned from the conceptual help, the $MaximumHistoryCount
preference variable default value is 4096, since
Windows PowerShell 3.0. This is how many commands that PowerShell will save for the session.
You can change this in your $profile
to a maximum of 32767. You can verify this is the maximum via the following
command and seeing the MaxRange attribute.
(Get-Variable MaximumHistoryCount).Attributes
History Class
PowerShell can access command history through the Microsoft.PowerShell.Commands
namespace with the [HistoryInfo]
class.
The class has the following properties.
Property | Type | Description |
---|---|---|
Id | [long] |
Id of the history entry |
CommandLine | [string] |
String of the command |
ExecutionStatus | [System.Management.Automation.Runspaces.PipelineState] |
Execution status of associated pipeline |
StartExecutionTime | [datetime] |
Start time of associated pipeline |
EndExecutionTime | [datetime] |
End time of associated pipeline |
Duration | [timespan] |
Calculated timespan from start and end time |
Note: The Duration
property was first available in PowerShell Core 6.1. It is displayed by default in
Get-History
output since PowerShell 7.0.0-preview.2.
It was added by PowerShell community member Keith Hill. Anyone can contribute to make PowerShell
even greater!
Get-History
When you run Get-History
or its alias h
, you should see something similar to following output. That is to say, your
specific data will be different, but the same properties should be present. For versions below 7 preview 2, you will
only see the Id
and CommandLine
properties.
Id Duration CommandLine
-- -------- -----------
7 0.001 Clear-History
8 0.077 Get-Command -Noun History
9 0.001 $PSVersionTable.PSVersion.ToString()
10 10.001 Start-Sleep -Seconds 10
But wait, there’s more!
Piping the command to Format-List
, you’ll see all of the properties.
Id : 7
CommandLine : Clear-History
ExecutionStatus : Completed
StartExecutionTime : 11/20/2019 10:53:25 PM
EndExecutionTime : 11/20/2019 10:53:25 PM
Duration : 00:00:00.0016020
Id : 8
CommandLine : Get-Command -Noun History
ExecutionStatus : Completed
StartExecutionTime : 11/20/2019 10:53:55 PM
EndExecutionTime : 11/20/2019 10:53:55 PM
Duration : 00:00:00.0770803
Id : 9
CommandLine : $PSVersionTable.PSVersion.ToString()
ExecutionStatus : Completed
StartExecutionTime : 11/20/2019 10:54:10 PM
EndExecutionTime : 11/20/2019 10:54:10 PM
Duration : 00:00:00.0015147
Id : 10
CommandLine : Start-Sleep -Seconds 10
ExecutionStatus : Completed
StartExecutionTime : 11/20/2019 10:54:28 PM
EndExecutionTime : 11/20/2019 10:54:38 PM
Duration : 00:00:10.0010661
Send Specific Historical Commands to the Clipboard
Now that we know how to get the commands from history, here is how you can get exactly what you want into your clipboard.
# short version
h 75,76,77,61,62,63,65,74 | select -exp CommandLine | clip
# full version
Get-History -Id 75,76,77,61,62,63,65,74 | Select-Object -ExpandProperty CommandLine | clip
This grabbed the history of the lines I typed/pasted in, in the order I wanted them, expanded the CommandLine property, and sent it to the clipboard.
Pretty simple.
Note: By the way, I use | clip
a lot instead of using the mouse to select and copy. Doing this has save me a considerable
amount of time and without error.
Get a Range of HistoryInfo Entries
I just learned while writing this post that you can get a range of history entries.
# short version
h 44 -c 7
# long version
Get-History -Id 44 -Count 7
The Id
that you supply is the last entry. The Count
is the number of preceding history entries, including the
Id
.
Invoke-History
The Cmdlet Invoke-History
runs commands from the session history.
Re-execute Specific Historical Commands
What if I wanted to re-execute those lines in the same order?
# short version
75,76,77,61,62,63,65,74 | % {r $_ }
# full version
75,76,77,61,62,63,65,74 | ForEach-Object { Invoke-History -Id $_ }
Send History into the Pipeline
If you remember from the help for Invoke-History
, you’ll see that Id
can accept pipeline input via ByPropertyName
.
This means that you could do the following.
h 75 | r
This will execute the command associated with history entry with Id
of 75.
Unfortunately, you cannot send an array of [HistoryInfo]
objects into Invoke-History
as it only accepts a single
instance.
A Better History
If you want better control and multi-session spanning history, you should use PSReadLine
. And if you are using
PowerShell 6 or higher, or are running Windows 10 October 2018 Update (build 1809) or higher, you already have
PSReadLine
installed and are most likely using it.
Here are the properties that have something do to with history.
- AddToHistoryHandler
- HistoryNoDuplicates
- HistorySavePath
- HistorySaveStyle
- HistorySearchCaseSensitive
- HistorySearchCursorMovesToEnd
- HistoryStringComparison
- MaximumHistoryCount
Getting this list was a little tricky, but here’s how you can do it. This method will help you digging into other objects as well.
# get the object type from the command output
(Get-PSReadLineOption).GetType().FullName
# get all of the attributes from the object type
[Microsoft.PowerShell.PSConsoleReadLineOptions] | Format-List
# get the list of all properties
([Microsoft.PowerShell.PSConsoleReadLineOptions]).DeclaredProperties
# get the list of all properties, where the Name property contains the word history
([Microsoft.PowerShell.PSConsoleReadLineOptions]).DeclaredProperties.Name.Where{$_ -match 'history'}
To learn more about PSReadLine
, check out the README file for the project’s GitHub repo.
Here are a few blog posts about PSReadLine
, albeit dated. They should, however, still provide relevant information.
- Towards a better console - PSReadLine for PowerShell command line editing
- PSReadLine - A free PowerShell console extension
- Change PowerShell’s Tab Complete
Summary
We used PowerShell’s Help system to view conceptual help and the help for the *-History
cmdlets. We saw examples of
using the two primary cmdlets, Get-History
and Invoke-History
, including how to save or re-execute specific
commands.
I hope you’ve found this interesting or informative. If you have any comments or questions, please post them below.
Thanks for reading!
Credit: The overlay is a clock image from Fabrizio Verrecchia.
Leave a comment