Windows PowerShell Tip of the Week

Here’s a quick tip on working with Windows PowerShell. These are published every week for as long as we can come up with new tips. If you have a tip you’d like us to share or a question about how to do something, let us know.

Find more tips in the Windows PowerShell Tip of the Week archive.

Automatic Script Writing Using Get-History

The Get-History cmdlet is one of the unsung heroes of Windows PowerShell. After all, Get-History enables you to retrieve a step-by-step recap of all the commands that have been issued during the current PowerShell session. Who cares about that? Well, believe it or not, you do. For example, suppose you’re working on a particularly-complicated command, and you finally get it to work. You run a bunch of other commands, then decide to re-run that complicated command again. How do you do that? Well, one way is to call Get-History and get back a list of all the commands you issued during that session:

Id CommandLine
-- -----------
 1 cd c:\scripts
 2 get-childitem -recurse
 3 cls

Note. Well, OK, so maybe none of those are particularly-complicated commands, but you get the idea.

Once you known the command ID you can then pass that value to the Invoke-History cmdlet and re-run the script. Want to get a list of all the items in the C:\Scripts folder? This command will do that for you:

Invoke-History 2

Nice, huh?

As it is, Get-History is practically the perfect cmdlet; in fact, about the only thing it doesn’t do is write your scripts for you.

Well, not unless you ask it, that is.

As you no doubt know by now, commands typed at the PowerShell prompt can typically be used as-is in a Windows PowerShell script. In fact, that’s one way you can write a PowerShell script: keep trying things at the command prompt, command-by-command, until you’ve managed to achieve your goal. Once you’ve achieved success, you can review the history, copy the commands that worked, then paste those commands into a .PS1 file.

Or, you could simply run a script like this one:

$a = Get-History

foreach ($i in $a)
    {Add-Content C:\Scripts\Test.ps1 $i.CommandLine}

So what are we doing here? To begin with, we’re using the Get-History cmdlet to retrieve the history for the current PowerShell session; we’re then stashing that information in a variable named $a. When this is done, $a will be an array, with each item in the array corresponding to one of the commands in our PowerShell history. And because $a is an array, that means we can set up a foreach loop to walk through each item in the array. Inside that loop we use this command to, one-by-one, add the history items to a file named C:\Scripts\Test.ps1:

{Add-Content C:\Scripts\Test.ps1 $i.CommandLine}

Note. If the file Test.ps1 already exists, then the Add-Content cmdlet will simply append each item in the array as a new line in the file. If Test.ps1 doesn’t exist, then Add-Content will first create the file and then start adding the new lines.

What will that give us? We’re glad you asked; that will give us a file that looks like this:

cd c:\scripts
get-childitem -recurse
cls

Needless to say, that also gives us a script that’s ready for use.

Of course, it’s possible that this script also includes a lot of commands that you don’t want to use. But that’s fine; use Get-History to write all the commands to the .PS1 file, then open the file in Notepad and simply delete the unwanted lines. This will still be faster than copying and pasting individual lines, and will be way faster – and far more foolproof – then typing all those lines of code in yourself.

Or, again, use the range operator to retrieve just a subset of the commands in the PowerShell history:

$a = Get-History (4..19)

And for those of you who like to type as little as possible, it turns out that you can do this all in a single line of code:

foreach ($i in Get-History){Add-Content C:\Scripts\Test.ps1 $i.CommandLine}

With this command we don’t bother retrieving the history and storing it in a variable; instead, we simply reference the Get-History cmdlet in our foreach loop:

foreach ($i in Get-History)

That’s one of the cool things about Windows PowerShell: you can often-times use a cmdlet in places where you would typically have to use a variable.

Admittedly, this might not be the most important use of Windows PowerShell that anyone has ever come up with. (But, then again, maybe it is; who are we to judge?) On the other hand, it is a kind of cool demonstration of how PowerShell can be used to do things other than read from event logs or stop services. And that’s definitely worth something, right?

See you next week.