Summary: Learn how to use Windows PowerShell to import and to export command history and avoid writing scripts.

 

Hey, Scripting Guy! QuestionHey, Scripting Guy! I have worked with other systems where I was able to keep track of the commands that I typed into the console. The neat thing about that was that I could export my commands, and import them into another console and replay my command history. This was a great way to perform repetitive configuration tasks without having to write a script. Is this something that can be accomplished with Windows PowerShell?

-- SW

 

Hey, Scripting Guy! AnswerHello SW, Microsoft Scripting Guy Ed Wilson here. Windows PowerShell has great command history features. By learning how to use the command history features of Windows PowerShell, many repetitive tasks can be automated.

Portions of today’s Hey, Scripting Guy! Blog post are adapted from material in my Windows PowerShell 2.0 Best Practices book, published by Microsoft Press in December 2009.

Much administrative work with Windows PowerShell consists of typing a series of commands at the console. Whether it is editing the registry, stopping various processes and services, the configuration work has to be replicated to several different servers to make sure a consistent operating environment. In the past such duplication of effort would require creating scripts. If the commands to be duplicated are a series of commands typed at the console, you can use the command history mechanism to replace the need for a script. To do this, use the Get-History cmdlet and export the commands to an xml file as seen here.

Get-History | Export-Clixml -Path C:\fso\history.xml

 

The result is an XML file that represents all the commands which have been typed at the console. The resulting xml file is seen in the following figure.

 

After you have created a command history xml file, you can import the commands from the xml file by using the Import-Clixml cmdlet. You pipeline the results of the Import-Clixml cmdlet to the Add-History cmdlet to add the commands back to the command history. The trick is to use the –passthru switch  so that the commands will go to both the Add-History cmdlet and to the ForEach-Object cmdlet as well. In the Foreach-Object cmdlet you use the Invoke-History cmdlet to run each command that is in the history. The commands are shown here, as are the results of running them.

PS C:\> Import-Clixml -Path C:\fso\history.xml | Add-History -Passthru |
>> ForEach-Object { Invoke-History }
>>
 if(!(test-path -path c:\fso4)) { new-item c:\fso4 -ItemType directory }


    Directory: C:\


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----          1/9/2009  12:33 AM            fso4
Get-Command >> C:\fso4\commands.txt
notepad C:\fso4\commands.txt

 

This technique will work remotely by using the Invoke-Command cmdlet. The thing to keep in mind is that the path statement is relative to the computer that is the target, not the computer that is executing the command. If you do not keep this in mind, then an error such as the one seen in the following figure appears.

 

If you copy the file to the target machine first, and adjust your command line, the import and execute history technique works well. The good thing about Windows PowerShell, is that you can use an UNC path with the Copy-Item cmdlet. It is this feature that actually makes the technique worthwhile because it enables you to easily move a file to a remote computer. This is shown here.

PS C:\> Copy-Item C:\fso\history.xml \\berlin\c$\fso
PS C:\> Import-Clixml -Path C:\fso\history.xml | Add-History -Passthru | ForEach
-Object { Invoke-History }
 if(!(test-path -path c:\fso4)) { new-item c:\fso4 -ItemType directory }


    Directory: C:\


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----          1/9/2009  12:40 AM            fso4
Get-Command >> C:\fso4\commands.txt
notepad C:\fso4\commands.txt

 

Fan out commands are commands that are launched from a central computer and run against a number of remote computers. One way to perform this technique is to use the Invoke-Command cmdlet as seen here.

PS C:\> Invoke-Command -Computer berlin,vista -Script `
>> {"$env:computername $(get-date)" }
>>
VISTA 01/09/2009 08:31:42
BERLIN 01/09/2009 08:31:47

 

You can use fan out commands by specifying an array of computer names for the computername parameter for many of the cmdlets. The problem with this approach is the results are almost useless. An example may show the issue that is involved. In the command below, the Get-Service cmdlet is used to obtain service configuration information from two computers. The first is a computer named Vista, and the second one is a server named Berlin. As you can see from the partial output, copied here, the results of the command are merged, and there is no column that shows the computer name that the result is associated with. The results are interesting because you can quickly look at the service name between two computers and easily see divergent configurations. The fan out command and a truncated result set is seen here.

PS C:\> Get-Service -ComputerName Vista, Berlin

Status   Name               DisplayName
------   ----               -----------
Running  1-vmsrvc           Virtual Machine Additions Services ...
Running  1-vmsrvc           Virtual Machine Additions Services ...
Running  AeLookupSvc        Application Experience
Stopped  AeLookupSvc        Application Experience
Stopped  ALG                Application Layer Gateway Service
Stopped  ALG                Application Layer Gateway Service
Stopped  Appinfo            Application Information
Stopped  Appinfo            Application Information
Stopped  AppMgmt            Application Management
Stopped  AppMgmt            Application Management
Stopped  AudioEndpointBu... Windows Audio Endpoint Builder
Stopped  AudioEndpointBu... Windows Audio Endpoint Builder
Stopped  Audiosrv           Windows Audio
Stopped  Audiosrv           Windows Audio
Running  BFE                Base Filtering Engine
Running  BFE                Base Filtering Engine
Running  BITS               Background Intelligent Transfer Ser...
Stopped  BITS               Background Intelligent Transfer Ser...
Stopped  Browser            Computer Browser
Running  Browser            Computer Browser
>>> Results trimmed >>>

 

You can see from the results of the Get-Service command that the AeLookupSvc is running on the first computer, and stopped on the second computer. To check this, it is simple to use the Get-Service cmdlet to connect to each of the computers, and to check the status of the service.

PS C:\> Get-Service -Name AeLookupSvc -computer vista

Status   Name               DisplayName
------   ----               -----------
Stopped  AeLookupSvc        Application Experience


PS C:\> Get-Service -Name AeLookupSvc -computer Berlin

Status   Name               DisplayName
------   ----               -----------
Running  AeLookupSvc        Application Experience

 

You might think that the first instance of the service name belongs to the computer that is listed first. As you can see, the AeLookupSvc is running on Berlin, but is stopped on Vista. This is the same order that is seen in the original output. However, the Vista computer was listed first in the fan out command. Perhaps this means that the second computer results are listed first, and the first computer results are listed second—a Last In First Out (LIFO) operation. Before assuming such to be the case, you have to check another service. In the output from the original fan out command, the Bits service was listed first as running, and second as stopped. To see the status of the Bits service on Berlin and on Vista, you can use the following two commands.

PS C:\> Get-Service -Name Bits -computer berlin

Status   Name               DisplayName
------   ----               -----------
Stopped  BITS               Background Intelligent Transfer Ser...


PS C:\> Get-Service -Name Bits -computer Vista

Status   Name               DisplayName
------   ----               -----------
Running  BITS               Background Intelligent Transfer Ser...

 

You can see that the Bits service is stopped on Berlin, and is running on Vista. The results of using Get-Service as a fan out command by supplying an array of computer names to the computername parameter brings back interesting results, but results that are meaningless when it comes to checking the exact status of a service on a remote computer. As a best practice, you should pipeline the results of the fan out command to a Format-Table cmdlet and choose the machineName property. The value of displayName property is the same value that is seen in the Services MMC in the name column. The command and a truncated output are shown here.

PS C:\> Get-Service -ComputerName berlin,vista | format-table name, status, mach
inename, displayName -AutoSize

Name                            Status MachineName DisplayName
----                            ------ ----------- -----------
1-vmsrvc                       Running vista       Virtual Machine Additions...
1-vmsrvc                       Running berlin      Virtual Machine Additions...
AeLookupSvc                    Running berlin      Application Experience
AeLookupSvc                    Stopped vista       Application Experience
ALG                            Stopped berlin      Application Layer Gateway...
ALG                            Stopped vista       Application Layer Gateway...
Appinfo                        Stopped berlin      Application Information
Appinfo                        Stopped vista       Application Information
AppMgmt                        Stopped vista       Application Management
AppMgmt                        Stopped berlin      Application Management
AudioEndpointBuilder           Stopped berlin      Windows Audio Endpoint Bu...
AudioEndpointBuilder           Stopped vista       Windows Audio Endpoint Bu...
Audiosrv                       Stopped berlin      Windows Audio
Audiosrv                       Stopped vista       Windows Audio
BFE                            Running vista       Base Filtering Engine
BFE                            Running berlin      Base Filtering Engine
BITS                           Stopped berlin      Background Intelligent Tr...
BITS                           Running vista       Background Intelligent Tr...
Browser                        Running vista       Computer Browser
Browser                        Stopped berlin      Computer Browser

 

Because the value of the displayName property is frequently quite long, it does not always easily fit within the confines of an 80 column display. If you have it selected early in the order of the properties to be selected by the Format-Table cmdlet, you will probably end up with several columns not being displayed. This is shown here.

PS C:\> Get-Service -ComputerName berlin,vista | format-table name, displayname,
 status, machinename -AutoSize

WARNING: 2 columns do not fit into the display and were removed.

Name                           DisplayName
----                           -----------
1-vmsrvc                       Virtual Machine Additions Services Application
1-vmsrvc                       Virtual Machine Additions Services Application
AeLookupSvc                    Application Experience
AeLookupSvc                    Application Experience

 

As you can see, this defeats the purpose of choosing the machineName property in the first place, if it is left off because it does not fit on the display. To correct this potential problem, it is a best practice to always select the property that uses the longest values to be displayed for the last position in the command. In this way, you allow Windows PowerShell to truncate the property value rather than filling up the screen with information, you could easily infer from a truncated display.

The other solution to the problem of the shrinking display output would be not to use the autosize parameter of the Format-Table cmdlet. You could use the wrap parameter instead. When this parameter is used, single line entries are allowed to wrap and to form multiple lines. Depending on the information that you are looking for, this output can be either helpful, or annoying. Here is an example of using the wrap parameter.

PS C:\> Get-Service -ComputerName berlin,vista | format-table name, displayname,
 status, machinename -Wrap

Name                DisplayName                      Status MachineName
----                -----------                      ------ -----------
1-vmsrvc            Virtual Machine Add             Running vista
                    itions Services App
                    lication
1-vmsrvc            Virtual Machine Add             Running berlin
                    itions Services App
                    lication
AeLookupSvc         Application Experie             Running berlin
                    nce
AeLookupSvc         Application Experie             Stopped vista
                    nce

 

At this point in the discussion, you may be thinking that you can really resolve this problem of the truncated display output by using both the autosize and the wrap parameters. This would enable the output to maximize the display real estate (the function of autosize) and also to allow for multiline wrapping (the function of wrap). This never works, but does not generate an error. Windows PowerShell will give priority to the autosize parameter and ignore the wrap parameter. It does not matter in which order the two parameters are typed; the wrap parameter will be ignored.

SW, that is all there is to importing and exporting Windows PowerShell command history. To Script or Not to Script week will continue tomorrow when I will talk about how to work with Active Directory Domain Services.

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

 

Ed Wilson, Microsoft Scripting Guy