Saving PowerShell commands across sessions






3.86/5 (3 votes)
A set of functions to provide the ability to save commands to be used in future PowerShell sessions.
Introduction
PowerShell is a useful tool for developers and system administrators alike. Much of its usefulness comes from being able to craft a complicated, multi-part command to get some useful piece of information. The problem is that once I close the window, the command is gone. If I really want to save that command, I could make a script, but that involves taking several steps to open a text editor, type in the command, and save the script file. Furthermore, I then have to type in the full path of the script when I want to run it later. Also, when I make a script, I feel the need to include descriptive comments such as the purpose of and arguments to the script. All I really want is a quick and easy way to save commands and execute them later.
Background
Doing a little searching on the web revealed the Get-History cmdlet, which allows you to see the entire command history that has been saved so far. There is also the Add-History cmdlet, but my attempts to use it to load in saved commands proved unsuccessful. The commands would be shown by Get-History but did not appear to be accessible via the up or down keys, which is the normal way to cycle through the history.
Even if it had worked, I wanted a way to be able to save an arbitrary number of commands and be able to execute them easily without having to arrow through the history. Another shortcoming of that approach is that the loaded commands would eventually be pushed out of the history, requiring some method to reload the commands into the history. With those limitations in mind, I set about writing my own functions.
Why functions and not a set of script files? The main reason for using functions instead of script files is that I can call them from any location without having to type the path to the directory where I keep my scripts. Once the functions have been added to the $profile file, the functions will be loaded every time PowerShell starts, and be available for use without any further intervention from me. Since I am too lazy to type out the entire function names every time, I also set a short alias in the $profile file for each of the functions. For those not as familiar with PowerShell, it is as simple as this:
Set-Alias nsc NewSaveCommand
The key to making this all work is that PowerShell has an eval type function. The Invoke-Expression cmdlet will take a string either through the pipeline or as an argument and execute it as a PowerShell command. This allows you to save a command into a small text file and later load and execute the command.
Using the Code
There are four functions needed for the full set of features. They are:
NewSavedCommand
RemoveSavedCommand
GetSavedCommand
ExecuteSavedCommand
The function of each should be clear from the name, but the way they work is worth explaining.
NewSavedCommand
will save the last command entered (before the command to save the last command, of course) to a text file in the same directory as your PowerShell profile file. The name of the file is in the form <number>.psc, where the number chosen is the smallest number that is not already taken. This means that if you have several commands and start deleting them, the next commands you add will fill in the blank numbers instead of being added to the end while leaving gaps.
RemoveSavedCommand
will remove a saved command that you no longer want to keep around. This will free up the command number to be used again by the next command you save. If you pass a negative number to the function, all of your saved commands will be deleted. You have been fairly warned.
GetSavedCommand
can either print a list of all saved commands or can print out the entire text of a particular saved command. In the screenshot, you can see the results of passing no argument (or a negative one) to the function. One of my commands is quite long and goes off the screen. To see the entire command, I simply call the function again with the number of the command I want to see. That command is one that I will probably want to call throughout the next few weeks, and you can see how useful it is to not have to remember and retype the entire thing.
ExecuteSavedCommand
takes the number of the command to be executed as a parameter and will read in the contents of <number>.psc and pass that to Invoke-Expression. The result will be that your complicated, specially crafted command will be executed, without having to type in the entire text again.
Points of Interest
One interesting thing I came across while making these functions is the ability to format your output as a table without having predefined classes and properties. The first call to GetSavedCommand
in the screenshot shows the results. Here's the corresponding code:
Get-ChildItem (Split-Path $profile -parent) *.psc |
Format-Table @{Label='Number';
Expression={[int]([System.IO.Path]::GetFileNameWithoutExtension($_.FullName))}},
@{Label='Command'; Expression={Get-Content $_.FullName}} -auto
If you are not familiar with PowerShell, a few notes about the syntax are in order. @{}
creates a hashtable that can be filled with key-value pairs. The semicolon is used as a separator because the comma is already used to make arrays (no braces or parentheses are needed to make an array, just a list of comma separated values). For those used to coding in C style languages, you may guess that the Expression
value is a block of code, and you would be correct.
Format-Table is a built-in cmdlet that allows you to pass the object to be formatted through the pipeline and specify the properties as arguments. If you want to show something that is not a property, however, you will need to use the hashtable syntax shown above. Label
is the name that will be shown at the top of the column and Expression
is a code block that calculates the value for that column. The -auto
argument simply tells the cmdlet to auto-size the columns to prevent a lot of whitespace.
Room for Improvement
After creating the functions, I thought of a few potential improvements that I decided not to implement for my own use. They should not be too difficult to add, and I would be willing to add them if several people would find them useful.
- Ability to save more than just the last command entered. This would probably be implemented by passing the ID of the command returned from the Get-History cmdlet to the
NewSavedCommand
function. - Ability to pass arguments to the saved commands.
History
- 21 May 2008
- Initial version.