12

I found this excellent looking method of persisting history in Windows PowerShell.

# Persistent History
# Save last 200 history items on exit
$MaximumHistoryCount = 200
$historyPath = Join-Path (split-path $profile) history.clixml

# Hook powershell's exiting event & hide the registration with -supportevent (from nivot.org)
Register-EngineEvent -SourceIdentifier powershell.exiting -SupportEvent -Action {
    Get-History -Count $MaximumHistoryCount | Export-Clixml (Join-Path (split-path $profile) history.clixml)
}

# Load previous history, if it exists
if ((Test-Path $historyPath)) {
    Import-Clixml $historyPath | ? {$count++;$true} | Add-History
    Write-Host -Fore Green "`nLoaded $count history item(s).`n"
}

# Aliases and functions to make it useful
New-Alias -Name i -Value Invoke-History -Description "Invoke history alias"
Rename-Item Alias:\h original_h -Force
function h { Get-History -c  $MaximumHistoryCount }
function hg($arg) { Get-History -c $MaximumHistoryCount | out-string -stream | select-string $arg }

However, when I paste this into my $PROFILE and restart PowerShell I get the following error:

Register-EngineEvent : Missing an argument for parameter 'Action'. Specify a parameter of type
'System.Management.Automation.ScriptBlock' and try again.
At D:\path\to\WindowsPowerShell\Microsoft.PowerShell_profile.ps1:20 char:73
+ Register-EngineEvent -SourceIdentifier powershell.exiting -SupportEvent -Action
+                                                                         ~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Register-EngineEvent], ParameterBindingException
    + FullyQualifiedErrorId : MissingArgument,Microsoft.PowerShell.Commands.RegisterEngineEventCommand
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
alexleonard
  • 1,314
  • 3
  • 21
  • 37
  • FYI, PowerShell v3 fixes a number of issues you're trying to work around. $MaximumHistoryCount is 4096 by default and by default Get-History returns all items. – Keith Hill May 10 '13 at 04:33
  • > $PSVersionTable.PSVersion Major = 3, Minor = 0, Build = -1, Revision = -1 – alexleonard May 10 '13 at 04:51
  • 1
    Cool. Then you can skip modifying $MaximumHistoryCount and Get-History will return all history for you (well, up to 4096 items). Unless that is, you want to limit how much is saved. :-) – Keith Hill May 10 '13 at 04:55
  • 1
    Here's [my history export/import](https://github.com/aarismendi/ps-scripts/blob/master/Microsoft.PowerShell_profile.ps1) feel free to steal :P – Andy Arismendi May 10 '13 at 07:07
  • 1
    @AndyArismendi you don't have a license stated anywhere, so I can't use it – Demi Jul 14 '14 at 00:52
  • Related: For all PowerShell history of all time, see *[How can I see the command history across all PowerShell sessions in Windows Server 2016?](https://stackoverflow.com/questions/44104043)* – Peter Mortensen Dec 18 '18 at 07:13

3 Answers3

10

Very related to this question is "How do I access my history using the up/down arrows?" The PSReadLine module solves this:

Install-Package PSReadLine

Then add:

Import-Module PSReadLine

To your $profile.

mikemaccana
  • 110,530
  • 99
  • 389
  • 494
jhclark
  • 2,493
  • 1
  • 20
  • 14
3

Well, as a combination of the topicstarter's code and a slightly changed answer by Steven Penny, this is the full piece of code which is working for me

################# Persistent History ############
# Save last 200 history items on exit
$MaximumHistoryCount = 200    
$historyPath = Join-Path (split-path $profile) history.clixml

# Hook powershell's exiting event & hide the registration with -supportevent (from nivot.org)
Register-EngineEvent -SourceIdentifier powershell.exiting -SupportEvent -Action {
      Get-History | Export-Clixml $historyPath
}.GetNewClosure()

# Load previous history, if it exists
if ((Test-Path $historyPath)) {
    Import-Clixml $historyPath | ? {$count++;$true} | Add-History
    Write-Host -Fore Green "`nLoaded $count history item(s).`n"
}

# Aliases and functions to make it useful
New-Alias -Name i -Value Invoke-History -Description "Invoke history alias"
Rename-Item Alias:\h original_h -Force
function h { Get-History -c  $MaximumHistoryCount }
function hg($arg) { Get-History -c $MaximumHistoryCount | out-string -stream | select-string $arg }
1

This is the code I used to use when I persisted my history:

$historyPath = Join-Path (split-path $profile) "history-$(Get-Date -f o).clixml"
Register-EngineEvent -SourceIdentifier powershell.exiting -SupportEvent -Action {
    Get-History | Export-Clixml $historyPath
}.GetNewClosure()

The GetNewClosure() is used to capture the $historyPath variable IIRC.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Keith Hill
  • 194,368
  • 42
  • 353
  • 369
  • 1
    Hey thanks for the answer. Is that the whole code that you used? Is the blog post I'm following doing something unecessary with the section under # Load previous history, if it exists? – alexleonard May 10 '13 at 04:58
  • It's up to you but over time that history is going to get awfully large. I prefer to have a simple mechanism to search my persisted history. – Keith Hill May 10 '13 at 05:42
  • Cool. I'll test it out tomorrow when I'm back in the office. Thanks! – alexleonard May 10 '13 at 06:33
  • 1
    So I added the text exactly as you have it to my $PROFILE file and when I exit and restart PowerShell I still have no history access when I hit F7 or F8 – alexleonard May 12 '13 at 03:54
  • And all I need to add is the four lines you've posted here - or do I need to do something else as well? Cheers! – alexleonard Jun 10 '13 at 07:37
  • That's it. When you exit PowerShell (V3 in my case), you should see a history-xxxx.clixml file in your `$Home\Documents\WindowsPowerShell` directory. – Keith Hill Jun 10 '13 at 17:44
  • I might also point out that Keith's earlier answer [here](http://stackoverflow.com/a/1439174/115690) is better, because it prunes duplicates. :-) – Michael Sorens Jul 13 '13 at 23:44