Perserving Command History Across Sessions

Perserving Command History Across Sessions

  • Comments 27

<Edited 7/2/2006 to add tags and Categories>

Ben Winzenz didn't like the fact that Windows PowerShell did not maintain history lists between sessions (http://winzenz.blogspot.com/2006/06/cool-mshpowershell-tidbit.html) .   We hear you Ben.  Back to my least favorite phrase, "to ship is to choose".  That said, we try to give you in the community the power to do that which we cannot.  Here is some code that you can put in your profile file that defines a function BYE which saves off your sessions history into a History.CSV file in your home directory and then adds that to your history when you startup the next session.  You get to control how much of the history you want (up to 32KB-1) but I just do 1KBs worth.

$MaximumHistoryCount = 1KB

if (!(Test-Path ~\PowerShell -PathType Container))
{   New-Item ~\PowerShell -ItemType Directory
}

function bye
{   Get-History -Count 1KB |Export-CSV ~\PowerShell\history.csv
    exit
}

if (Test-path ~\PowerShell\History.csv)
{   Import-CSV ~\PowerShell\History.csv |Add-History
}

I was going to put comments in the code but thought I would test out my assertion that VERBOSITY produces self-documenting scripts.  I think it does but you can let me know one way or the other.

So add this to your startup and then do a session, type BYE instead of EXIT and then start a new session and do a Get-History.   You'll see a bunch of commands already in your list. 

Now lets have a little fun with History, do what I instructed above and get into your new session.  Now do a Get-History and pipe it to Get-Member:


PS> Get-History |Get-Member -MemberType Property
   TypeName: Microsoft.PowerShell.Commands.HistoryInfo

Name               MemberType Definition
----               ---------- ----------
CommandLine        Property   System.String CommandLine {get;}
EndExecutionTime   Property   System.DateTime EndExecutionTime {get;}
ExecutionStatus    Property   System.Management.Automation.Runspaces.Pip...
Id                 Property   System.Int64 Id {get;}
StartExecutionTime Property   System.DateTime StartExecutionTime {get;}

This means that you can find out when something was executed (e.g. which session it happened in) using the following command:

PS> ghy |ft id,endexecutiontime,commandline -auto

 Id EndExecutionTime      CommandLine
 -- ----------------      -----------
612 6/29/2006 5:39:34 AM  gcm export-csv |fl *
613 6/30/2006 6:51:16 PM  ipconfig
614 6/30/2006 8:51:38 PM  cd C:\kits
...

Enjoy!
Jeffrey P. Snover
Windows PowerShell Architect

PSMDTAG:FAQ: How do I perserve command history across sessions?

PSMDTAG:FAQ:  Can I run an exit script?


PSMDTAG:ENVIRONMENT: History

PSMDTAG:SHELL: shutdown, startup, exitScript

Leave a Comment
  • Please add 5 and 5 and type the answer here:
  • Post
  • Will there ever be a way to run PowerShell via STDIN / STDOUT rather than in a Win32 console - such that it works in an Cygwin rxvt terminal? Currently, PowerShell appears to use Win32 Console commands to read input - which doesn't work when it's not attached to a console.

    I know that it would be far from ideal, but if it used STDIN/STDOUT then one could write a wrapper program that uses Readline and thus supplies nice history etc. support, and allow PowerShell to run in an rxvt terminal.

    My bash/rxvt already saves history between sessions, and it can save an arbitrary number of entries (more than 32KB - it works on lines rather than bytes). Rxvt also has nice paging support with Shift+PgUp/PgDn, which is one of the big reasons I don't use the Win32 console. Another big reason is that QuickEdit mode (i.e. mouse causing selection in Win32 console) causes the running application to freeze, while selecting in rxvt copies to the clipboard straight away, no need for pausing or anything. Also, rxvt has bold and underline, as well as colors.

    I've tried to use PowerShell, and it's the Win32 console window that's holding me back. I abandoned the Win32 console many years ago.

    Here's the stack trace of PowerShell frozen in an rxvt session:

    ---8<---
    ntkrnlpa.exe!KiSwapContext+0x2e
    ntkrnlpa.exe!KiSwapThread+0x46
    ntkrnlpa.exe!KeWaitForSingleObject+0x1c2
    ntkrnlpa.exe!NtRequestWaitReplyPort+0x63d
    ntkrnlpa.exe!KiFastCallEntry+0xf8
    ntdll.dll!KiFastSystemCallRet
    ntdll.dll!ZwRequestWaitReplyPort+0xc
    ntdll.dll!CsrClientCallServer+0x8c
    kernel32.dll!ReadConsoleInternal+0x1be
    kernel32.dll!ReadConsoleW+0x42
    mscorwks.dll!CallDescrWorker+0x33
    mscorwks.dll!CallDescrWorkerWithHandler+0xa3
    mscorwks.dll!MethodDesc::CallDescr+0x19c
    mscorwks.dll!MethodDesc::CallTargetWorker+0x20
    mscorwks.dll!MethodDescCallSite::Call+0x18
    mscorwks.dll!InvokeImpl+0x43f
    mscorwks.dll!RuntimeMethodHandle::InvokeMethodFast+0xbd
    mscorlib.ni.dll+0x30f629
    --->8---

    You can see in there the all-important call to ReadConsoleW rather than ReadFileW.
  • Ahh - that rocks.

    Thanks for this info.  I very much appreciate it.
  • It sure would be nice to be able to hook a "PSExiting" event in a profile script and then save history right before exit.
  • BTW, I would change this:

    Get-History -Count 1KB

    to

    Get-History -Count $MaximumHistoryCount
  • > It sure would be nice to be able to hook a "PSExiting" event in a
    > profile script and then save history right before exit.

    This requires the EVENTING architecture that we'll be working on for V2

    RE: -COUNT $MaximumHistoryCount
    Great catch!

    Jeffrey Snover
    Windows PowerShell Architect
  • For this kind of occasion, I have been thinking about asking this question:

    Is there a way to know that powershell  is exiting and run whatever the scripts needed to be run instead of using such custom function as "BYE" to persist certain PowerShell states?

    Since PowerShell reads profile when starting up, how about a profile file for when PowerShell is "exiting"?(if that functionality had been there, we could execute those commands for persisting history list across sessions.
  • > Is there a way to know that powershell  is exiting and run whatever the scripts needed to be
    > run instead of using such custom function as "BYE" to persist certain PowerShell states?

    This is a perfectly reasonable expectation.  We wanted to do this but didn't have the time for V1.0

    Jeffrey Snover
    Windows PowerShell Architect
  • Just wanted to leave a quick comment letting you know that the code you included didn't work for me.  I am using the Exchange Management Shell (Exchange 2007), and it was throwing errors on a few things.

    First, it didn't like the $MaximumHistoryCount = 1KB.  I changed it to $MaximumHistoryCount = 1000 and it was happy.

    Second, the code:
    if (!(Test-Path ~\PowerShell -PathType Container))
    {   New-Item ~\PowerShell -ItemType Directory
    }

    also resulted in an error.  Specifically, "A parameter cannot be found that matches parameter 'PathType'"

    I simply removed those lines, and everything seems to work fine now.  Here is the resulting code that I am using in the Exchange Management Shell.

    $MaximumHistoryCount = 1000

    function bye
    {   Get-History -Count $MaximumHistoryCount |Export-CSV ~\history.csv
       exit
    }

    if (Test-path ~\History.csv)
    {   Import-CSV ~\History.csv |Add-History
    }
  • Barrkel:  There are two ways to use PowerShell without the Win32 console -- either by hosting the engine yourself, or by using the -command parameter.  See if this helps your cause:

    http://www.leeholmes.com/blog/UsingMshexeInteractivelyFromWithinOtherPrograms.aspx

    Lee
  • bwinzenz:

    Jeffrey is giving you a sneak preview of an upcoming build :)  The Exchange Management shell will soon have this same syntax, but for now you'll have to replace:

    1kb with 1k

    As for the -PathType option, you can determine the parameter by typing the following:

    (get-command test-path).Definition

    One of those should be close to "PathType" -- we went through a renaming phase a while back, which is why you see the difference.

  • Hi and thanks for the code. But I have a problem with the imported history: it can't be traversed interactively (with the up and down arrows). is there a way to let the console know about these new commands in history?

  • In the interests of finding an existing object - perhaps the title could be better expressed as "Preserving Command History ..."

    Regards from Aotearoa

    Andy

  • See if this blog entry helps. Perserving Command History Across Sessions [link]

  • Yeah, I second the PSExiting hook request. Is there a general way to hook events in powershell? Some searching turned out dry.

  • > Is there a general way to hook events in powershell?

    Not in V1.  We'll take that as a V2 feature request.  We checked in that code last night.

    Pretty responsive eh?

    Jeffrey Snover [MSFT]

    Windows Management Partner Architect

    Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell

    Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx

Page 1 of 2 (27 items) 12