Use PowerShell Commands from One Session in Another Session

Use PowerShell Commands from One Session in Another Session

  • Comments 2
  • Likes

Summary: Learn how to automatically save commands from one session, and then use them in a new Windows PowerShell session.

Microsoft Scripting Guy, Ed Wilson, is here. One of the fun things about getting to travel around and talk to people who are using Windows PowerShell on a daily basis to automate their systems is the quality of the questions I receive. One question that has come up several times in the last month that the Scripting Wife and I have been traveling is, “How can I save my history when I exit Windows PowerShell, and then have that history available to me when I open up Windows PowerShell again?”

I gave this some thought…

I decided that it would be trivial to import automatically saved history when Windows PowerShell starts, and that the issue would be saving the Windows PowerShell history when Windows PowerShell exits. One student who was in my class in Irvine, California suggested that I automatically save each command into a history file as I type the commands. In this way, when I exit the Windows PowerShell console, I will already have an up-to-date history file. While this technique is not impossible, it could have an unintended performance hit, and I decided against it.

Another idea I had was to use the Windows PowerShell transcript. It is easy to start the transcript each time Windows PowerShell starts by adding the Start-Transcript command to the Windows PowerShell profile. I could then parse the Windows PowerShell transcript and pull out all of the commands. After I had all of the commands, it would be possible to add them to the history. But that would require creating a HistoryInfo object.

In the end, I decided to create two functions and add them to my profile. The first function imports a saved history.xml file into the current Windows PowerShell session. The second function exports the current history to a history.xml file, and then it exits Windows PowerShell.

The trick is to call the function that exports the history.xml file prior to exiting Windows PowerShell instead of clicking the “X” on the Windows PowerShell console, or typing exit to exit Windows PowerShell. As always, when I create a function, I also like to create an alias for that function. The two commands from my profile that create the aliases are shown here.

New-Alias -Name eps -Value Exit-PsWithHistory -description "mred alias"

New-Alias -Name ips -Value Import-PSHistory -Description "mred alias"

Instead of automatically importing the saved history, I manually type the alias for my import saved history function. The reason for this is that I do not always want to import my saved history; but, this is simply the way I work. I could easily modify my profile so that I do import my history automatically, and then I could simply clear the history if I did not want to use it. It will require a bit of testing before I make up my mind as to which action is most efficient.

Here is my function to export command history to a history.xml file and exit Windows PowerShell.

Function Exit-PsWithHistory

{

 If(!(Test-Path $PSHistory))

  {New-Item -path $PSHistory -itemtype directory}

  Get-History -count $MaximumHistoryCount | Export-Clixml -Path (Join-Path $PSHistory -child history.xml)

 Exit

} #end function Exit-PsWithHistory

The Exit-PsWithHistory function relies on the $PSHistory variable. This is a variable that I define in my Windows PowerShell profile, and it points to the folder I use to store my history.xml file. Here is the command that creates the $PSHistory variable.

$PSHistory = Join-path -path (split-path $PROFILE) -ChildPath history

So, I use the Test-Path cmdlet to see if there is a folder named History in my Windows PowerShell profile folder. If it does not exist, I create it by using the New-Item cmdlet.

If(!(Test-Path $PSHistory))

  {New-Item -path $PSHistory -itemtype directory}

I then use the Get-History cmdlet to get all of the items in my command history. By default, the Get-History cmdlet returns only 32 items from the history. If I want to get all of the commands in my command history, I have to specify a value for the Count parameter. The most logical thing to do is to use the $MaximumHistoryCount variable to specify this number. In this way, if I increase the maximum history count from the default value of 64 to another number, my function will always export all of the commands. I use the Export-CliXML cmdlet to export my command history into an .xml file, and I use Join-Path to create the path to my file. This command is shown here.

Get-History -count $MaximumHistoryCount | Export-Clixml -Path (Join-Path $PSHistory -child history.xml)

The last thing I do is call the exit command to exit Windows PowerShell.

The function to import my saved history.xml file appears here.

Function Import-PsHistory

{

 If(Test-Path $PSHistory)

 {

  Import-Clixml -Path (Join-Path -path $PSHistory -child history.xml) |

  Add-history

 }

} #end function import-psHistory

Once again, I use the Test-Path cmdlet to ensure that the history folder exists. If it does, I assume that a history.xml file exists. This is not a major problem, because the only reason the History folder would exist, would be if I had created it. If I did create it, it should have been when I was exporting a history.xml file.

If(Test-Path $PSHistory)

The next thing I do is import the xml file, and pipeline it to the Add-History cmdlet. Here is that portion of the function.

Import-Clixml -Path (Join-Path -path $PSHistory -child history.xml) |

  Add-history

I bump up my maximum history by assigning a new value to the variable. Rather than typing a big, long, complicated number, I simply use the kb administrative constant to allow me to create 2048 history entries. This command is shown here.

$MaximumHistoryCount = 2kb

You might wonder, "How large can I create the $maximumHistoryCount variable?" To determine the maximum allowed value, I use the Get-Variable cmdlet. This command is shown here.

Get-Variable MaximumAliasCount | select -ExpandProperty attributes

 

                 MinRange                                MaxRange TypeId

                 --------                                -------- ------

                     1024                                   32768 System.Management.Automation.Validat...

One thing to keep in mind, is when you use the Get-Variable cmdlet, do not include the dollar sign prefix. If I do include the dollar sign prefix of the variable, I obtain a rather cryptic error that states Windows PowerShell cannot find a variable with the name of 2048. Because I recognize that number as the value I had increased the $maximumhistorycount variable to, it makes sense. I then drop the dollar sign, and return the psvariable object. I then send the variable to the Format-List cmdlet, and I choose all of the properties. The result reveals that there is an object hiding in the attributes variable. I then pipe the psvariable object to the Select-Object cmdlet, and I use the ExpandProperty parameter to expand the object that is stored in the attributes property. These commands are shown here.

get-variable maximumhistorycount

get-variable maximumhistorycount | fl *

get-variable maximumhistorycount | select -ExpandProperty attributes

The commands and their associated output are shown here.

Image of command output

In the following image, I show my profile with the two new functions, the two aliases, and the two variable assignments.

Image of script

To exit Windows PowerShell, I use the eps alias (my alias for the Export-PsWithHistory function. When I start Windows PowerShell, my profile runs, and it loads the functions, aliases, and variables into memory. I then type the Import-PsHistory command (I can also use the ips alias). After I do that, I populate my history with all of my previous commands. I use the h (alias for Get-History) command to see what commands I now have available to me in my command history. This sequence of commands is shown in the image that follows.

Image of command output

There is one downside to this technique: Imported commands (via Add-History) do not populate the up and down arrows. But, dude (or dudette), with 2048 potential commands in the command history, that would be a ridiculous amount of Up and Down arrowing; that is why there are single letter aliases for Get-History and for Invoke-History.

Join me tomorrow for the Weekend Scripter when I will explore more coolness related to Windows PowerShell.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Hi Ed,

    this is another thing to consider.

    We can save the command history before exiting the ISE ( or whenever appropriate ) and get it back if we want to! Wonderful and well done!

    But ... I'm not sure why I would want to have it back next time I start PS again ...

    Maybe there are some commands I'd like to recycle next time.

    Probably the lengthy and complicated ones that could save me a bit of typing time.

    But if they are really identical and I want to use them frequently, I would have saved them to a script file. Most likely I would have to use different literals for certain objects that could have been parameterized in scripts.

    Definitely I can't remmeber exactly what I typed in last time, so scrolling/searching through the history would be necessary and making the history large will increase the time to find the right command.

    One other thing to mention is that getting back my old complicated command line from last weeks' session could be a dangerous thing if I don't inspect the command carefully and have a close look at where I am eg. in my file hierarchy or on which PS-Drive the old command may run and do something that is not wanted in my current context. Especially if we are using relative paths like in "remove-item -recurse *.*" it might have made sense in my last session having set the working directory to "$env:temp" but it might do no good in my new session being in "$env:systemroot".

    Having an old history loaded can be handy, of course ... but the advantages may be limited. Having scripts (snippets), tab expansion and copy&paste available today it could well be sufficient to work this way.

    Klaus

  • @Klaus Schulte I completely agree with you. One needs to be aware of what is going on in the command history. For example if I have several thousand commands in a history file, the commands, in all probability, will not work until I have replicated the previous environment. For example, suppose I store an object in a variable in one command. In another command, I call a method from that object. I cannot run the second command, until the previous command runs. Personally, I seldom export and import my previous command history ... but the people who asked me the question, were really happy to get this solution. This is one of the really cool things about PowerShell -- it lets you work the way that you want to. If one does not like the default behavior, one can nearly always change things. Cheers.