Have you forgotten a command while in the command-line, or do you wish you had saved history, PowerShell history to the rescue!
Not a reader? Watch this related video tutorial!In this tutorial, learn how to run prior commands, import and export, and clear history. By the end of the article, you won’t have to rely solely on the up and down arrow keys to retrieve history anymore!
Prerequisites
To follow along with this tutorial, be sure you Windows PowerShell on Windows 10 or PowerShell 7.x on any supported platform.
Retrieving Command History
If you have ever lost a command, you may want to save it to find later. If so, PowerShell saves time by saving your history for quick retrieval. To see your saved history, PowerShell has the Get-History
cmdlet.
Before retrieving any history, you first need to build some, as the history is restarted on every console session. To do so, copy and run the code below to add history entries; any code will do. Commands need to exist before Get-History
has anything to return!
Get-Service -ServiceName 'BITS'
Get-Service | Where-Object -Property Name -Like "Microsoft*"
Get-Service | Where-Object -FilterScript {($_.Status -eq 'Running') -and ($_.StartType -eq 'Manual')}
Now that you have run some commands, as shown below, you have history to work with.
Be mindful of sensitive commands that may contain info not meant for prying eyes. For example, if you do not use secure strings in your code, sensitive information such as a password or API secret will be visible as plain text in the history file!
Now use the Clear-Host command to tidy up the screen and further demonstrate the history features.
Clear-Host
Clearing the screen doesn’t mean that all history entries are gone. To find the commands previously cleared, hit the up ↑ or down ↓ arrows to quickly view the console hosts’ command history.
Using the up and down arrows is great when finding a single command you ran previously. But what about re-running more than one command? In that case, to display your full session history, type in the Get-History
command as seen below, which presents a list of previously run commands.
As shown below, Get-History
returns not only the commands that were run but also a command ID
and how long that command took to execute. The command ID
will come in handy later.
Get-History
The maximum default history entries are
4096
as stored in theMaximumHistoryCount
variable. Change this value to allow up to32767
entries with the command:Set-Variable MaximumHistoryCount 32767
.
By default, the Get-History
cmdlet doesn’t return all object properties. To return all object properties, pipe the history output to the Select-Object
cmdlet. Piping the output to the Select-Object
cmdlet returns the StartExecutionTime
and EndExecutionTime
properties and are referenced to determine the Duration
.
Get-History | Select-Object -Property *
Running Previously Executed Commands
Now that you can retrieve older commands, what good is that? Most of the time, PowerShell users need to execute those commands. Luckily, you can do so without copying/pasting. Remembering a previous command or series of commands may be difficult. To run a previous command, execute the Invoke-History
cmdlet.
If you have been following along, you will have a previous Get-Service
command in your history. As mentioned earlier, each history entry has an associated ID
.
Perhaps you need to re-run one of the Get-Service
commands you ran earlier. In that case, copy and run the code below to re-run the second history entry, as shown in the below demonstration.
Invoke-History -Id 2
Your
ID
value may need to change depending on your personal command history.
Specifying a value of 2
for the ID
parameter, the Invoke-History
cmdlet immediately re-runs the command as if you had typed the command directly into the console.
In addition to a number, the
ID
parameter also accepts a pattern to match against. For example, runningInvoke-History -Id 'Get-Service'
will find all history entries starting with that value.
Exporting PowerShell Command History
After a long work session, you may want to save and export your commands. If so, you’re in luck. PowerShell can export nearly anything to a text file; command history is no different. When working with command history, common formats include CSV or XML.
Save your PowerShell history for future use, as shown below. Piping Get-History
to the Export-CSV
cmdlet allows you to save the history entries to a file. In this example, PowerShell saves the file to C:\Temp, but you can save the command history anywhere.
Get-History | Export-CSV -Path 'C:\Temp\CommandHistory.Csv'
To export the history as XML: Get-History | Export-CliXml -Path ‘C:\Temp\CommandHistory.xml’
Clearing Command History
Over time your history may become cluttered or you may have run a command with a sensitive value that is now saved to history. In either case, you probably want to remove those entries from your command history. To do that, to clear the console history, with the Clear-History
cmdlet.
By default, the Clear-History
command removes all console history. But, what if you don’t want to delete all history? In that case, selectively clear history with a specific entry ID
, a specified Count
of entries, or only the Newest
set of entries.
If you don’t want to type in or locate a specific set of entries, pass an array of wildcard patterns to the CommandLine
parameter to remove all matched commands.
Clear-History -CommandLine *Help*, *Syntax
There is another type of history, which is saved to disk via the PSReadLine module. The Clear-History
cmdlet only clears the current in-memory console history, but you may also want to clear all saved disk history entries.
To clear all history except for the last run command, use the PSReadline method by running:
[Microsoft.PowerShell.PSConsoleReadLine]::ClearHistory()
Before clearing the disk history, you must find where it is saved. To do that, copy and run the following command to retrieve the history file disk location.
$History = (Get-PSReadLineOption).HistorySavePath
$History
The default PSReadLine Windows path is stored in the
HistorySavePath
variable and is: %userprofile%\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt
Now open the history file in notepad, similar to what is shown below. Each history entry is added below the last saved command.
Now that you have seen where and what is in the history file, remove the history file with the Remove-Item
cmdlet, as shown below.
Remove-Item -Path $History -Verbose
Upon running another command, the history file will be regenerated.
Importing Command History
If you have opened a new console session or have previously saved a complicated series of commands, you may want to import them into your current history. To import those commands into your current console history, leverage the Add-History
command.
To import prior history, you first need an exported history file. In a previous section, you saved a series of commands in a CSV file, and perhaps you need to import those saved commands. To import the previous CSV file and add history entries to the current session, run the below code, adjusting your CSV path as necessary.
$O$OldHistory = Import-Csv -Path 'C:\Temp\CommandHistory.Csv'
Add-History -InputObject $OldHistory -Passthru
As shown below, PowerShell imported the previous commands into console history and displayed them with the PassThru
parameter.
Not all commands return the original objects passed in. By adding the PassThru
parameter, available on some cmdlets, the original object is returned.
Considering Security and PowerShell History
Despite the usefulness of PowerShell history, there are security considerations to be aware of. You will want to be mindful when entering sensitive commands, as those commands are saved to the plaintext command history file, subject to scrutiny.
Pretend for a moment you are a ‘Red Teamer‘. You want to do some recon on a target user. You may be interested in commands they run, passwords and API keys they may be passing through the session during a normal day.
Here’s a sample of what you could see looking at a target’s history file in real time, by just reloading the text file in Notepad++:
Now you have a password that the target is using. Just watching the rest of the session history and you’ll more than likely see where that password is being used.
Don’t be alarmed by what you’ve seen. Knowing what you know now, you can make better decisions.
How do you securely handle secrets? By retrieving credentials from disk or keyboard input with the Get-Credential cmdlet. Secure those API keys and other secrets by converting to a secure string with the ConvertTo-SecureString cmdlet. Both cmdlets will not display the secret itself in history. PowerShell Secrets Management is also a good choice.
In any environment, turning on ScriptBlock and Module logging is prudent. With this logging, PowerShell is a poor choice for an attacker to use, as every run of code is stored for later retrieval.
What’s Next?
You should now know how the PowerShell history commands work. Now the next time you lose an important command or need to save your session’s history, you have no excuse!
If you need to clear all history except for the last run command, use the PSReadline method by running