Above all else, the design of Windows PowerShell places priority on its use as an efficient and powerful interactive shell. Even its scripting language plays a critical role in this effort, as it too heavily favors interactive use.
What surprises most people when they first launch PowerShell is its similarity to the command prompt that has long existed as part of Windows. Familiar tools continue to run. Familiar commands continue to run. Even familiar hotkeys are the same. Supporting this familiar user interface, though, is a powerful engine that lets you accomplish once cumbersome administrative and scripting tasks with ease.
This chapter introduces PowerShell from the perspective of its interactive shell.
You rely on a lot of effort invested in your current tools. You have traditional executables, Perl scripts, VBScript, and of course, a legacy build system that has organically grown into a tangled mess of batch files. You want to use PowerShell, but you don’t want to give up everything you already have.
To run a program, script, batch file, or other executable command in the system’s path, enter its filename. For these executable types, the extension is optional:
Program.exe arguments ScriptName.ps1 arguments BatchFile.cmd arguments
To run a command that contains a space in its
name, enclose its filename in single-quotes ('
) and
precede the command with an ampersand (&
), known in PowerShell as the
invoke operator:
& 'C:\Program Files\Program\Program.exe' arguments
To run a command in the current directory,
place .\
in front of its filename:
.\Program.exe arguments
To run a command with spaces in its name from
the current directory, precede it with both an ampersand and
.\
:
& '.\Program With Spaces.exe' arguments
In this case, the solution is mainly to use your current tools as you always have. The only difference is that you run them in the PowerShell interactive shell, rather than cmd.exe.
The final three tips in the solution merit special attention. They are the features of PowerShell that many new users stumble on when it comes to running programs. The first is running commands that contain spaces. In cmd.exe, the way to run a command that contains spaces is to surround it with quotes:
"C:\Program Files\Program\Program.exe"
In PowerShell, though, placing text inside quotes is part of a feature that lets you evaluate complex expressions at the prompt, as shown in Example 1-1.
So, a program name in quotes is no different
from any other string in quotes. It’s just an expression. As shown
previously, the way to run a command in a string is to precede that
string with the invoke operator
(&
). If the command you want to run is a batch
file that modifies its environment, see Program: Retain Changes to Environment Variables Set by a Batch
File.
Note
By default, PowerShell’s security policies prevent scripts from running. Once you begin writing or using scripts, though, you should configure this policy to something less restrictive. For information on how to configure your execution policy, see Enable Scripting Through an Execution Policy.
The second command that new users (and seasoned veterans before coffee!) sometimes stumble on is running commands from the current directory. In cmd.exe, the current directory is considered part of the path: the list of directories that Windows searches to find the program name you typed. If you are in the C:\Programs directory, cmd.exe looks in C:\Programs (among other places) for applications to run.
PowerShell, like most Unix shells, requires
that you explicitly state your desire to run a program from the current
directory. To do that, you use the .\Program.exe
syntax, as shown previously.
This prevents malicious users on your system from littering your hard
drive with evil programs that have names similar to (or the same as)
commands you might run while visiting that directory.
To save themselves from having to type the location of commonly used scripts and programs, many users put these utilities along with their PowerShell scripts in a “tools” directory, which they add to their system’s path. If PowerShell can find a script or utility in your system’s path, you do not need to explicitly specify its location.
Note
Scripts and examples from this book are available at http://www.oreilly.com/catalog/9780596801519.
To learn how to write a PowerShell script, see Write a Script.
You have a command line that works from cmd.exe, and want to resolve errors that occur from running that command in PowerShell.
Enclose any affected command arguments in single quotes to prevent them from being interpreted by PowerShell, and replace any single quotes in the command with two single quotes.
PS > cmd /c echo '!"#$%&''()*+,-./09:;<=>?@AZ[\]^_`az{|}~' !"#$%&'()*+,-./09:;<=>?@AZ[\]^_`az{|}~
One of PowerShell’s primary goals has always been command consistency. Because of this, cmdlets are very regular in the way that they accept parameters. Native executables write their own parameter parsing, so you never know what to expect when working with them. In addition, PowerShell offers many features that make you more efficient at the command line: command substitution, variable expansion, and more. Since many native executables were written before PowerShell was developed, they may use special characters that conflict with these features.
Note
Unlike it does with cmdlets, PowerShell doesn’t apply special parameter processing to native executables. However, version one of PowerShell did incorrectly process some arguments that contained the colon character if the argument looked like parameters for a cmdlet. If you’ve experienced this (or perhaps formed a superstition that “calling native executables doesn’t work properly”), try again.
As an example, the command given in the Solution uses all the special characters available on a typical keyboard. Without the quotes, PowerShell treats some of them as language features, as shown in Table 1-1.
When surrounded by single quotes, PowerShell accepts these characters as written, without the special meaning.
Despite these precautions, you may still
sometimes run into a command that doesn’t seem to work when called from
PowerShell. To see exactly what PowerShell passes
to that command, you can view the output of the trace source called
NativeCommandParameterBinder
:
PS > Trace-Command NativeCommandParameterBinder { cmd /c echo '!"#$%&''()*+,-./09:;<=>?@AZ[\]^_`az{|}~' } -PsHost DEBUG: NativeCommandParameterBinder Information: 0 : WriteLine Argument 0: /c DEBUG: NativeCommandParameterBinder Information: 0 : WriteLine Argument 1: echo DEBUG: NativeCommandParameterBinder Information: 0 : WriteLine Argument 2: !#$%&'()*+,-./09:;<=>?@AZ[\]^_`az{|}~ !"#$%&'()*+,-./09:;<=>?@AZ[\]^_`az{|}~
If the command arguments shown in this output don’t match the arguments you expect, they have special meaning to PowerShell and should be escaped.
To run a PowerShell command, type its name at the command prompt. For example:
PS > Get-Process Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------ ----- ----- ----- ------ -- ----------- 133 5 11760 7668 46 1112 audiodg 184 5 33248 508 93 1692 avgamsvr 143 7 31852 984 97 1788 avgemc
The Get-Process
command is an example of a native
PowerShell command, called a cmdlet. As compared to traditional
commands, cmdlets provide significant benefits to both administrators
and developers:
Because the Get-Process
cmdlet generates rich object-based
output, you can use its output for many process-related tasks.
The Get-Process
cmdlet is just one of the many
that PowerShell supports. See Find a Command to Accomplish a Task to learn
techniques for finding additional commands that PowerShell supports.
For more information about working with classes from the .NET Framework, see Work with .NET Objects.
Invoke the command as a Job
to have PowerShell run it in the background:
PS > Start-Job { while($true) { Get-Random; Start-Sleep 5 } } -Name Sleeper Id Name State HasMoreData Location -- ---- ----- ----------- -------- 1 Sleeper Running True localhost PS > Receive-Job Sleeper 671032665 1862308704 PS > Stop-Job Sleeper
PowerShell’s job cmdlets provide a consistent
way to create and interact with background tasks. In the Solution, we
use the Start-Job
cmdlet to launch a background
job on the local computer. We give it the name of
Sleeper
, but otherwise we don’t customize much of its
execution environment.
In addition to allowing you to customize the
job name, the Start-Job
cmdlet also lets you launch
the job under alternate user credentials or as a 32-bit process (if run
originally from a 64-bit process).
Once you have launched a job, you can use the
other Job
cmdlets to interact with it:
Note
In addition to the
Start-Job
cmdlet, you can also use the
-AsJob
parameter in many cmdlets to have them
perform their tasks in the background. Two of the most useful examples
are the Invoke-Command
cmdlet (when operating
against remote computers) and the set of WMI-related cmdlets.
If your job generates an error, the
Receive-Job
cmdlet will display it to you when you
receive the results, as shown in Example 1-2. If you want to investigate these
errors further, the object returned by Get-Job
exposes them through the Error
property.
Example 1-2. Retrieving errors from a Job
PS > Start-Job -Name ErrorJob { Write-Error Error! } WARNING: column "Command" does not fit into the display and was removed. Id Name State HasMoreData Location -- ---- ----- ----------- -------- 1 ErrorJob Running True localhost PS > Receive-Job ErrorJob Error! + CategoryInfo : NotSpecified: (:) [Write-Error], WriteError Exception + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorExc eption,Microsoft.PowerShell.Commands.WriteErrorCommand PS > $job = Get-Job ErrorJob PS > $job | Format-List * State : Completed HasMoreData : False StatusMessage : Location : localhost Command : Write-Error Error! JobStateInfo : Completed Finished : System.Threading.ManualResetEvent InstanceId : 801e932c-5580-4c8b-af06-ddd1024840b7 Id : 1 Name : ErrorJob ChildJobs : {Job2} Output : {} Error : {} Progress : {} Verbose : {} Debug : {} Warning : {} PS > $job.ChildJobs[0] | Format-List * State : Completed StatusMessage : HasMoreData : False Location : localhost Runspace : System.Management.Automation.RemoteRunspace Command : Write-Error Error! JobStateInfo : Completed Finished : System.Threading.ManualResetEvent InstanceId : 60fa85da-448b-49ff-8116-6eae6c3f5006 Id : 2 Name : Job2 ChildJobs : {} Output : {} Error : {Microsoft.PowerShell.Commands.WriteErrorException,Microso ft.PowerShell.Commands.WriteErrorCommand} Progress : {} Verbose : {} Debug : {} Warning : {} PS > $job.ChildJobs[0].Error Error! + CategoryInfo : NotSpecified: (:) [Write-Error], WriteError Exception + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorExc eption,Microsoft.PowerShell.Commands.WriteErrorCommand PS >
As this example shows, jobs are sometimes
containers for other jobs, called child jobs. Jobs
created through the Start-Job
cmdlet will always be
child jobs attached to a generic container. To access the errors
returned by these jobs, you instead access the errors in its first child
job (called child job number zero).
Use the
Register-TemporaryEvent
command given in Create a Temporary Event Subscription to register for the
event’s StateChanged
event:
PS > $job = Start-Job -Name TenSecondSleep { Start-Sleep 10 } PS > Register-TemporaryEvent $job StateChanged -Action { [Console]::Beep(100,100) Write-Host "Job #$($sender.Id) ($($sender.Name)) complete." } PS > Job #6 (TenSecondSleep) complete. PS >
When a job completes, it raises a
StateChanged
event to notify subscribers that its
state has changed. We can use PowerShell’s event handling cmdlets to
register for notifications about this event, but they are not geared
toward this type of one-time event handling. To solve that, we use the
Register-TemporaryEvent
command given in Create a Temporary Event Subscription.
In our example action block in that solution, we simply emit a beep and write a message saying that the job is complete.
As another option, you can also update your
prompt
function to highlight jobs that are complete
but still have output you haven’t processed:
$psJobs = @(Get-Job -State Completed | ? { $_.HasMoreData }) if($psJobs.Count -gt 0) { ($psJobs | Out-String).Trim() | Write-Host -Fore Yellow }
For more information about events and this type of automatic event handling, see Chapter 31.
You want to customize PowerShell’s interactive experience with a personalized prompt, aliases, and more.
When you want to customize aspects of
PowerShell, place those customizations in your personal profile script.
PowerShell provides easy access to this profile script by storing its
location in the $profile
variable.
Note
By default, PowerShell’s security policies prevent scripts (including your profile) from running. Once you begin writing scripts, though, you should configure this policy to something less restrictive. For information on how to configure your execution policy, see Enable Scripting Through an Execution Policy.
To create a new profile (and overwrite one if it already exists):
New-Item -type file -force $profile
notepad $profile
Get-ChildItem $profile
Once you create a profile script, you can add
a function called Prompt
that returns
a string. PowerShell displays the output of this function as your
command-line prompt.
function Prompt { "PS [$env:COMPUTERNAME] >" }
This example prompt displays your computer
name, and looks like PS
[LEE-DESK]>
.
You may also find it helpful to add aliases to your profile. Aliases let you refer to common commands by a name that you choose. Personal profile scripts let you automatically define aliases, functions, variables, or any other customizations that you might set interactively from the PowerShell prompt. Aliases are among the most common customizations, as they let you refer to PowerShell commands (and your own scripts) by a name that is easier to type.
Note
If you want to define an alias for a command but also need to modify the parameters to that command, then define a function instead.
Set-Alias new New-Object Set-Alias iexplore 'C:\Program Files\Internet Explorer\iexplore.exe'
Your changes will become effective once you save your profile and restart PowerShell. To reload your profile immediately, run this command:
. $profile
Functions are also very common customizations,
with the most popular being the Prompt
function.
The Solution discusses three techniques to make useful customizations to your PowerShell environment: aliases, functions, and a hand-tailored prompt. You can (and will often) apply these techniques at any time during your PowerShell session, but your profile script is the standard place to put customizations that you want to apply to every session.
Note
To remove an alias or function, use the
Remove-Item
cmdlet:
Remove-Item function:\MyCustomFunction Remove-Item alias:\new
Although the Prompt
function
returns a simple string, you can also use the function for more complex
tasks. For example, many users update their console window title (by
changing the $host.UI.RawUI.WindowTitle
variable) or use
the Write-Host
cmdlet to
output the prompt in color. If your prompt function handles the screen
output itself, it still needs to return a string (for example, a single
space) to prevent PowerShell from using its default. If you don’t want
this extra space to appear in your prompt, add an extra space at the end
of your Write-Host
command and return
the backspace ("
`b"
) character, as shown in Example 1-3.
Example 1-3. An example PowerShell prompt
############################################################################## ## ## From Windows PowerShell Cookbook (O'Reilly) ## by Lee Holmes (http://www.leeholmes.com/guide) ## ############################################################################## Set-StrictMode -Version Latest function Prompt { $id = 1 $historyItem = Get-History -Count 1 if($historyItem) { $id = $historyItem.Id + 1 } Write-Host -ForegroundColor DarkGray "`n[$(Get-Location)]" Write-Host -NoNewLine "PS:$id > " $host.UI.RawUI.WindowTitle = "$(Get-Location)" "`b" }
In addition to showing the current location, this prompt also shows the ID for that command in your history. This lets you locate and invoke past commands with relative ease:
[C:\] PS:73 >5 * 5 25 [C:\] PS:74 >1 + 1 2 [C:\] PS:75 >Invoke-History 73 5 * 5 25 [C:\] PS:76 >
Although the profile referenced by $profile
is the one you will almost always
want to use, PowerShell actually supports four separate profile scripts.
For further details on these scripts (along with other shell
customization options), see Common Customization Points.
You want to accomplish a task in PowerShell but don’t know the command or cmdlet to accomplish that task.
Use the Get-Command
cmdlet to
search for and investigate commands.
To get the summary information about a specific command, specify the command name as an argument:
Get-Command CommandName
To get the detailed information about a
specific command, pipe the output of Get-Command
to the Format-List
cmdlet:
Get-Command CommandName
| Format-List
To search for all commands with a name that contains text, surround the text with asterisk characters:
Get-Command *text
*
To search for all commands that use the
Get
verb, supply Get
to the -Verb
parameter:
Get-Command -Verb Get
To search for all commands that act on a
service, use Service
as the value of the -Noun
parameter:
Get-Command -Noun Service
One of the benefits that PowerShell provides
administrators is the consistency of its command names. All PowerShell
commands (called cmdlets) follow a regular
Verb-Noun
pattern, for example: Get-Process
,
Get-EventLog
, and Set-Location
. The verbs come from a relatively
small set of standard verbs (as listed in Appendix J) and describe what action the
cmdlet takes. The nouns are specific to the cmdlet and describe what the
cmdlet acts on.
Knowing this philosophy, you can easily learn
to work with groups of cmdlets. If you want to start a service on the
local machine, the standard verb for that is Start
. A good guess would be to first try
Start-Service
(which in this case
would be correct), but typing Get-Command -Verb
Start
would also be an effective way to see what things you
can start. Going the other way, you can see what actions are supported
on services by typing Get-Command -Noun
Service
.
See Get Help on a Command for a way to list all commands along with a brief description of what they do.
The Get-Command
cmdlet is one of the three
commands you will use most commonly as you explore Windows PowerShell.
The other two commands are Get-Help
and Get-Member
.
There is one important point when it comes to
looking for a PowerShell command to accomplish a particular task. Many
times, that PowerShell command does not exist, because the task is best
accomplished the same way it always was: ipconfig.exe
to get IP configuration
information, netstat.exe
to list
protocol statistics and current TCP/IP network connections, and many
more.
For more information about the Get-Command
cmdlet, type Get-Help Get-Command
.
The command that provides help and usage
information about a command is called Get-Help
. It supports several different views
of the help information, depending on your needs.
To get the summary of help information for a
specific command, provide the command’s name as an argument to the
Get-Help
cmdlet. This primarily
includes its synopsis, syntax, and detailed description:
Get-Help CommandName
CommandName
-?
To get the detailed help information for a
specific command, supply the -Detailed
flag to the Get-Help
cmdlet. In
addition to the summary view, this also includes its parameter
descriptions and examples:
Get-Help CommandName
-Detailed
To get the full help information for a
specific command, supply the -Full
flag to the Get-Help
cmdlet. In
addition to the detailed view, this also includes its full parameter
descriptions and additional notes:
Get-Help CommandName
-Full
To get only the examples for a specific
command, supply the -Examples
flag to
the Get-Help
cmdlet:
Get-Help CommandName
-Examples
To retrieve the most up-to-date online version
of a command’s help topic, supply the -Online
flag to the Get-Help
cmdlet:
Get-Help CommandName
-Online
To find all help topics that contain a given
keyword, provide that keyword as an argument to the Get-Help
cmdlet. If the keyword isn’t also the
name of a specific help topic, this returns all help topics that contain
the keyword, including its name, category, and synopsis:
Get-Help Keyword
The Get-Help
cmdlet is the primary way to interact
with the help system in PowerShell. Like the Get-Command
cmdlet, the
Get-Help
cmdlet supports wildcards. If you want to
list all commands that match a certain pattern (for example,
*process*), you can simply type Get-Help *process*
.
Note
To generate a list of all cmdlets and aliases (along with their brief synopsis), run the following command:
Get-Help * -Category Cmdlet | Select-Object Name,Synopsis | Format-Table -Auto
If the pattern matches only a single command, PowerShell displays the help for that command. Although command wildcarding and keyword searching is a helpful way to search PowerShell help, see Program: Search Help for Text for a script that lets you search the help content for a specified pattern.
In addition to console-based help, PowerShell also offers online and Compiled Help (CHM) versions of its help content. The Solution demonstrates how to quickly access online help content, but accessing the CHM version of help is slightly more difficult.
If you are working within PowerShell’s Integrated Scripting Environment (ISE), accessing the CHM help is as easy as pressing F1.
If you are working on Windows 7, you can access the CHM help through the
Windows PowerShell Help
option in PowerShell’s jump list (Figure 1-1). To open PowerShell’s jump list, either right-click on the taskbar icon or click the arrow beside PowerShell’s icon in the start menu.If you are working within the PowerShell console, you can access the CHM help by launching the .CHM file directly. Program: View PowerShell’s HTML Help demonstrates how to do this.
The Get-Help
cmdlet is one of the three commands
you will use most commonly as you explore Windows PowerShell. The other
two commands are Get-Command
and
Get-
Member
.
For more information about the Get-Help
cmdlet, type Get-Help Get-Help
.
Both the Get-Command
and Get-Help
cmdlets let you search for command names that match a given pattern.
However, when you don’t know exactly what portions of a command name you
are looking for, you will more often have success searching through the
help content for an answer. On Unix systems, this
command is called Apropos
.
The Get-Help
cmdlet
automatically searches the help database for keyword references when it
can’t find a help topic for the argument you supply. In addition to that,
you might want to extend this even further to search for text
patterns or even help topics that talk
about existing help topics. PowerShell’s help
facilities don’t support this type of search.
That doesn’t need to stop us, though, as we can write the functionality ourselves.
To run this program, supply a search string to
the Search-Help
script (given
in Example 1-4). The search string can be either
simple text or a regular expression. The script then displays the name and
synopsis of all help topics that match. To see the help content for that
topic, use the Get-Help
cmdlet.
Example 1-4. Search-Help.ps1
############################################################################## ## ## Search-Help ## ## From Windows PowerShell Cookbook (O'Reilly) ## by Lee Holmes (http://www.leeholmes.com/guide) ## ############################################################################## <# .SYNOPSIS Search the PowerShell help documentation for a given keyword or regular expression. .EXAMPLE Search-Help hashtable Searches help for the term 'hashtable' .EXAMPLE Search-Help "(datetime|ticks)" Searches help for the term datetime or ticks, using the regular expression syntax. #> param( ## The pattern to search for [Parameter(Mandatory = $true)] $Pattern ) Set-StrictMode -Version Latest $helpNames = $(Get-Help * | Where-Object { $_.Category -ne "Alias" }) ## Go through all of the help topics foreach($helpTopic in $helpNames) { ## Get their text content, search for the specified pattern $content = Get-Help -Full $helpTopic.Name | Out-String if($content -match "(.{0,30}$pattern.{0,30})") { $helpTopic | Add-Member NoteProperty Match $matches[0].Trim() $helpTopic | Select-Object Name,Match } }
For more information about running scripts, see Run Programs, Scripts, and Existing Tools.
PowerShell’s compiled help (CHM) offers many useful features: a table of contents, an index, full-text search, and more. While easy to launch from the Integrated Scripting Environment (ISE) and the Windows 7 jump list, discoverability is still a problem from console windows.
To easily launch PowerShell’s CHM help, use the
Show-HtmlHelp
script shown in Example 1-5.
Example 1-5. Show-HtmlHelp.ps1
############################################################################## ## ## Show-HtmlHelp ## ## From Windows PowerShell Cookbook (O'Reilly) ## by Lee Holmes (http://www.leeholmes.com/guide) ## ############################################################################## <# .SYNOPSIS Launches the CHM version of PowerShell help. .EXAMPLE Show-HtmlHelp #> Set-StrictMode -Version Latest $path = (Resolve-Path c:\windows\help\mui\*\WindowsPowerShellHelp.chm).Path hh "$path::/html/defed09e-2acd-4042-bd22-ce4bf92c2f24.htm"
For more information about running scripts, see Run Programs, Scripts, and Existing Tools.
If you are browsing the filesystem with
Windows Explorer, typing PowerShell
into the address
bar launches PowerShell in that location (as shown in Figure 1-2).
The resulting session lacks the console window customizations defined by PowerShell’s Start menu link (as does launching PowerShell from the Start→Run dialog), but Customize the Shell to Improve Your Productivity shows how to make even these PowerShell windows display in PowerShell’s signature blue.
For another way to launch PowerShell from Windows Explorer, several members of the PowerShell community have written power toys and Windows Explorer extensions that provide a “Launch PowerShell Here” option when you right-click on a folder from Windows Explorer. An Internet search for “PowerShell Here” turns up several.
If you aren’t browsing the desired folder with
Windows Explorer, use PowerShell’s -NoExit
parameter, along with the implied
-Command
parameter. In the
-Command
parameter, call the Set-Location
cmdlet to initially move
to your desired location. From Start→Run
(or any other means of launching an application), type:
PowerShell -NoExit Set-Location 'C:\Program Files'
You want to invoke a PowerShell command or script from a batch file, a logon script, a scheduled task, or any other non-PowerShell application.
To invoke a PowerShell command, use the
-Command
parameter:
PowerShell -Command Get-Process; Read-Host
To launch a PowerShell script, use the
-File
parameter:
PowerShell -File 'full path to script' arguments
PowerShell -File 'c:\shared scripts\Get-Report.ps1' Hello World
By default, any arguments to
PowerShell.exe get interpreted as commands to run.
PowerShell runs the command as
though you had typed it in the interactive shell, and then exits. You
can customize this behavior by supplying other parameters to PowerShell.exe, such as
-NoExit
, -NoProfile
, and
more.
Since launching a script is so common,
PowerShell provides the -File
parameter to eliminate
the complexities that arise from having to invoke a script from the
-Command
parameter. This technique lets you invoke a
PowerShell script as the target of a logon script, advanced file
association, scheduled task, and more.
Note
When PowerShell detects that its input or
output streams have been redirected, it suppresses any prompts that it
might normally display. If you want to host an interactive PowerShell
prompt inside another application (such as Emacs), use
-
as the argument for the -File
parameter. In PowerShell (as with traditional Unix shells), this
implies “taken from standard input.”
powershell -File -
If the script is for background automation or a scheduled task, these scripts can sometimes interfere with (or become influenced by) the user’s environment. For these situations, three parameters come in handy:
- -NoProfile
Runs the command or script without loading user profile scripts. This makes the script launch faster, but it primarily prevents user preferences (e.g., aliases and preference variables) from interfering with the script’s working environment.
- -WindowStyle
Runs the command or script with the specified window style—most commonly
Hidden
. When run with a window style ofHidden
, PowerShell hides its main window immediately. For more ways to control the window style from within PowerShell, see Launch a Process.- -ExecutionPolicy
Runs the command or script with a specified execution policy applied only to this instance of PowerShell. This lets you write PowerShell scripts to manage a system without having to change the system-wide execution policy. For more information about scoped execution policies, see Enable Scripting Through an Execution Policy.
Note
If you are the author of the program that needs to run PowerShell scripts or commands, PowerShell lets you call these scripts and commands much more easily than calling its command-line interface. For more information about this approach, see Add PowerShell Scripting to Your Own Program.
If the arguments to the
-Command
parameter become complex, special character
handling in the application calling PowerShell (such as
cmd.exe) might interfere with the command you want
to send to PowerShell. For this situation, PowerShell supports an
EncodedCommand
parameter: a
Base64-encoded representation of the Unicode string you want to run.
Example 1-6 demonstrates
how to convert a string containing PowerShell commands to a
Base64-encoded form.
Once you have the encoded string, you can use
it as the value of the EncodedCommand
parameter, as shown in Example 1-7.
Example 1-7. Launching PowerShell with an encoded command from cmd.exe
Microsoft Windows [Version 6.0.6000] Copyright (c) 2006 Microsoft Corporation. All rights reserved. C:\Users\Lee>PowerShell -EncodedCommand MQAuAC4AMQAwACAAfAAgACUAIAB7ACAAIgBQAG8AdwBlAHIAUwBoA GUAbABsACAAUgBvAGMAawBzACIAIAB9AA== PowerShell Rocks PowerShell Rocks PowerShell Rocks PowerShell Rocks PowerShell Rocks PowerShell Rocks PowerShell Rocks PowerShell Rocks PowerShell Rocks PowerShell Rocks
For more information about running scripts, see Run Programs, Scripts, and Existing Tools.
You want to use the PowerShell console more efficiently for copying, pasting, history management, and scrolling.
Run the commands shown in Example 1-8 to permanently customize your PowerShell console windows and make many tasks easier.
Example 1-8. Set-ConsoleProperties.ps1
Push-Location Set-Location HKCU:\Console New-Item '.\%SystemRoot%_system32_WindowsPowerShell_v1.0_powershell.exe' Set-Location '.\%SystemRoot%_system32_WindowsPowerShell_v1.0_powershell.exe' New-ItemProperty . ColorTable00 -type DWORD -value 0x00562401 New-ItemProperty . ColorTable07 -type DWORD -value 0x00f0edee New-ItemProperty . FaceName -type STRING -value "Lucida Console" New-ItemProperty . FontFamily -type DWORD -value 0x00000036 New-ItemProperty . FontSize -type DWORD -value 0x000c0000 New-ItemProperty . FontWeight -type DWORD -value 0x00000190 New-ItemProperty . HistoryNoDup -type DWORD -value 0x00000000 New-ItemProperty . QuickEdit -type DWORD -value 0x00000001 New-ItemProperty . ScreenBufferSize -type DWORD -value 0x0bb80078 New-ItemProperty . WindowSize -type DWORD -value 0x00320078 Pop-Location
These commands customize the console color, font, history storage properties, QuickEdit mode, buffer size, and window size.
With these changes in place, you can also improve your productivity by learning some of the hotkeys for common tasks, listed in Table 1-2. PowerShell uses the same input facilities as cmd.exe, and so it brings with it all the input features that you are already familiar with—and some that you aren’t!
When you launch PowerShell from the link on your Windows Start menu, it customizes several aspects of the console window:
By default, these customizations do not apply when you run PowerShell from the Start→Run dialog. The commands given in the solution section improve the experience by applying these changes to all PowerShell windows that you open.
The hotkeys do, however, apply to all PowerShell windows (and any other application that uses Windows’ cooked input mode). The most common are given in the solution section, but Common Customization Points provides the full list.
In interactive use, full cmdlet names (such as Get-ChildItem
) are cumbersome and slow to type.
Although aliases are much more efficient, it takes a while to discover
them. To learn aliases more easily, you can modify your prompt to remind
you of the shorter version of any aliased commands that you use.
Add the program
Get-AliasSuggestion.ps1
, shown in Example 1-9, to your tools directory or another directory.Example 1-9. Get-AliasSuggestion.ps1
############################################################################## ## ## Get-AliasSuggestion ## ## From Windows PowerShell Cookbook (O'Reilly) ## by Lee Holmes (http://www.leeholmes.com/guide) ## ############################################################################## <# .SYNOPSIS Get an alias suggestion from the full text of the last command. Intended to be added to your prompt function to help learn aliases for commands. .EXAMPLE Get-AliasSuggestion Remove-ItemProperty Suggestion: An alias for Remove-ItemProperty is rp #> param( ## The full text of the last command $LastCommand ) Set-StrictMode -Version Latest $helpMatches = @() ## Find all of the commands in their last input $tokens = [Management.Automation.PSParser]::Tokenize( $lastCommand, [ref] $null) $commands = $tokens | Where-Object { $_.Type -eq "Command" } ## Go through each command foreach($command in $commands) { ## Get the alias suggestions foreach($alias in Get-Alias -Definition $command.Content) { $helpMatches += "Suggestion: An alias for " + "$($alias.Definition) is $($alias.Name)" } } $helpMatches
Add the text from Example 1-10 to the
Prompt
function in your profile. If you do not yet have aPrompt
function, see Customize Your Shell, Profile, and Prompt to learn how to add one.Example 1-10. A useful prompt to teach you aliases for common commands
function Prompt { ## Get the last item from the history $historyItem = Get-History -Count 1 ## If there were any history items if($historyItem) { ## Get the training suggestion for that item $suggestions = @(Get-AliasSuggestion $historyItem.CommandLine) ## If there were any suggestions if($suggestions) { ## For each suggestion, write it to the screen foreach($aliasSuggestion in $suggestions) { Write-Host "$aliasSuggestion" } Write-Host "" } } ## Rest of prompt goes here "PS [$env:COMPUTERNAME] >" }
For more information about running scripts, see Run Programs, Scripts, and Existing Tools.
Use the Get-ParameterAlias
script to return all
aliases for parameters used by the previous command in your session
history.
Example 1-11. Get-ParameterAlias.ps1
############################################################################## ## ## Get-ParameterAlias ## ## From Windows PowerShell Cookbook (O'Reilly) ## by Lee Holmes (http://www.leeholmes.com/guide) ## ############################################################################## <# .SYNOPSIS Looks in the session history, and returns any aliases that apply to parameters of commands that were used. .EXAMPLE PS >dir -ErrorAction SilentlyContinue PS >Get-ParameterAlias An alias for the 'ErrorAction' parameter of 'dir' is ea #> Set-StrictMode -Version Latest ## Get the last item from their session history $history = Get-History -Count 1 if(-not $history) { return } ## And extract the actual command line they typed $lastCommand = $history.CommandLine ## Use the Tokenizer API to determine which portions represent ## commands and parameters to those commands $tokens = [System.Management.Automation.PsParser]::Tokenize( $lastCommand, [ref] $null) $currentCommand = $null ## Now go through each resulting token foreach($token in $tokens) { ## If we've found a new command, store that. if($token.Type -eq "Command") { $currentCommand = $token.Content } ## If we've found a command parameter, start looking for aliases if(($token.Type -eq "CommandParameter") -and ($currentCommand)) { ## Remove the leading "-" from the parameter $currentParameter = $token.Content.TrimStart("-") ## Determine all of the parameters for the current command. (Get-Command $currentCommand).Parameters.GetEnumerator() | ## For parameters that start with the current parameter name, Where-Object { $_.Key -like "$currentParameter*" } | ## return all of the aliases that apply. We use "starts with" ## because the user might have typed a shortened form of ## the parameter name. Foreach-Object { $_.Value.Aliases | Foreach-Object { "Suggestion: An alias for the '$currentParameter' " + "parameter of '$currentCommand' is '$_'" } } } }
To make it easy to type command parameters,
PowerShell lets you type only as much of the command parameter as is
required to disambiguate it from other parameters of that command. In
addition to shortening implicitly supported by the shell, cmdlet authors
can also define explicit aliases for their parameters—for example,
CN
as a short form for
ComputerName
.
While helpful, these aliases are difficult to discover.
If you want to see the aliases for a specific command, you can
access its Parameters
collection:
PS > (Get-Command New-TimeSpan).Parameters.Values | Select Name,Aliases Name Aliases ---- ------- Start {LastWriteTime} End {} Days {} Hours {} Minutes {} Seconds {} Verbose {vb} Debug {db} ErrorAction {ea} WarningAction {wa} ErrorVariable {ev} WarningVariable {wv} OutVariable {ov} OutBuffer {ob}
If you want
to learn any aliases for parameters in your previous command, simply run
Get-ParameterAlias.ps1
. To make PowerShell do this
automatically, add a call to Get-ParameterAlias.ps1
in your prompt.
This script builds on two main features:
PowerShell’s Tokenizer API, and the rich
information returned by the Get-Command
cmdlet. PowerShell’s
Tokenizer API examines its input and returns PowerShell’s interpretation
of the input: commands, parameters, parameter values, operators, and
more. Like the rich output produced by most of PowerShell’s commands,
Get-Command
returns information about a command’s
parameters, parameter sets, output type (if specified), and
more.
For more information about the Tokenizer API, see Parse and Interpret PowerShell Scripts.
After working in the shell for a while, you want to invoke commands from your history, view your command history, and save your command history.
The shortcuts given in Customize the Shell to Improve Your Productivity let you manage your history, but PowerShell offers several features to help you work with your console in even more detail.
To get the most recent commands from your
session, use the Get-History
cmdlet:
Get-History
To rerun a specific command from your session
history, provide its Id
to the Invoke-History
cmdlet:
Invoke-History Id
To increase (or limit) the number of commands
stored in your session history, assign a new value to the $MaximumHistoryCount
variable:
$MaximumHistoryCount = Count
To save your command history to a file, pipe
the output of Get-History
to the
Export-CliXml
cmdlet:
Get-History | Export-CliXml Filename
To add a previously saved command history to
your current session history, call the Import-CliXml
cmdlet and then pipe that
output to the Add-History
cmdlet:
Import-CliXml Filename
| Add-History
To clear all commands from your session
history, use the Clear-History
cmdlet:
Clear-History
Unlike the console history hotkeys discussed
in Customize the Shell to Improve Your Productivity, the
Get-History
cmdlet produces rich
objects that represent information about items in your history. Each
object contains that item’s ID, command line, start of execution time,
and end of execution time.
Once you know the ID of a history item (as
shown in the output of Get-History
),
you can pass it to Invoke-History
to
execute that command again. The example prompt function shown in Customize Your Shell, Profile, and Prompt makes working with
prior history items easy—as the prompt for each command includes the
history ID that will represent it.
The IDs provided by the Get-History
cmdlet differ from the IDs given
by the Windows console common history hotkeys (such as F7), because
their history management techniques differ.
By default, PowerShell stores only the last 64
entries of your command history. If you want to raise or lower this
amount, set the $MaximumHistoryCount
variable to the size you desire. To make this change permanent, set the
variable in your PowerShell profile script.
After interactively experimenting at the command line for a while to solve a multistep task, you’ll often want to keep or share the exact steps you used to eventually solve the problem. The script smiles at you from your history buffer, but it is unfortunately surrounded by many more commands that you don’t want to keep.
To solve this problem, use the
Get-History
cmdlet to view the recent commands that
you’ve typed. Then, call Copy-History
with the IDs of the commands
you want to keep, as shown in Example 1-12.
Example 1-12. Copy-History.ps1
############################################################################## ## ## Copy-History ## ## From Windows PowerShell Cookbook (O'Reilly) ## by Lee Holmes (http://www.leeholmes.com/guide) ## ############################################################################## <# .SYNOPSIS Copy selected commands from the history buffer into the clipboard as a script. .EXAMPLE Copy-History Copies the entire contents of the history buffer into the clipboard. .EXAMPLE Copy-History -5 Copies the last five commands into the clipboard. .EXAMPLE Copy-History 2,5,8,4 Copies commands 2,5,8, and 4. .EXAMPLE Copy-History (1..10+5+6) Copies commands 1 through 10, then 5, then 6, using PowerShell's array slicing syntax. #> param( ## The range of history IDs to copy [int[]] $Range ) Set-StrictMode -Version Latest $history = @() ## If they haven't specified a range, assume it's everything if((-not $range) -or ($range.Count -eq 0)) { $history = @(Get-History -Count ([Int16]::MaxValue)) } ## If it's a negative number, copy only that many elseif(($range.Count -eq 1) -and ($range[0] -lt 0)) { $count = [Math]::Abs($range[0]) $history = (Get-History -Count $count) } ## Otherwise, go through each history ID in the given range ## and add it to our history list. else { foreach($commandId in $range) { if($commandId -eq -1) { $history += Get-History -Count 1 } else { $history += Get-History -Id $commandId } } } ## Finally, export the history to the clipboard. $history | Foreach-Object { $_.CommandLine } | clip.exe
For more information about running scripts, see Run Programs, Scripts, and Existing Tools.
To invoke a specific command by its
ID
:
Invoke-History ID
To search through your history for a command
containing text
:
PS > #text
<TAB>
To repopulate your command with the text of a
previous command by its ID
:
PS > #ID
<TAB>
Once you’ve had your shell open for a while, your history buffer quickly fills with useful commands. The history management hotkeys described in Customize the Shell to Improve Your Productivity show one way to navigate your history, but this type of history navigation works only for command lines you’ve typed in that specific session. If you keep a persistent command history (as shown in Save State Between Sessions), these shortcuts do not apply.
The Invoke-History
cmdlet illustrates the
simplest example of working with your command history. Given a specific
history ID (perhaps shown in your prompt function), calling
Invoke-History
with that ID will run that command
again. For more information about this technique, see Customize Your Shell, Profile, and Prompt.
As part of its tab-completion support,
PowerShell gives you easy access to previous commands as well. If you
prefix your command with the #
character, tab
completion takes one of two approaches:
- ID completion
If you type a number, tab completion finds the entry in your command history with that ID, and then replaces your command line with the text of that history entry. This is especially useful when you want to slightly modify a previous history entry, since
Invoke-History
by itself doesn’t support that.- Pattern completion
If you type anything else, tab completion searches for entries in your command history that contain that text. Under the hood, PowerShell uses the
-like
operator to match your command entries, so you can use all of the wildcard characters supported by that operator. For more information on searching text for patterns, see Search a String for Text or a Pattern.
PowerShell’s tab completion is largely driven
by the fully customizable TabExpansion
function. You
can easily change this function to include more advanced functionality,
or even just customize specific behaviors to suit your personal
preferences. For more information, see Tab Completion.
While PowerShell’s built-in filtering facilities are
incredibly flexible (for example, the Where-Object
cmdlet), they generally operate against specific properties of the
incoming object. If you are searching for text in the object’s formatted
output, or don’t know which property contains the text you are looking
for, simple text-based filtering is sometimes helpful.
To solve this problem, you can pipe the output
into the Out-String
cmdlet before passing it to
the Select-String
cmdlet.
Select-TextOutput
(shown in Example 1-13) does exactly this,
and it lets you search for a pattern in the visual representation of
command output.
Example 1-13. Select-TextOutput.ps1
############################################################################## ## ## Select-TextOutput ## ## From Windows PowerShell Cookbook (O'Reilly) ## by Lee Holmes (http://www.leeholmes.com/guide) ## ############################################################################## <# .SYNOPSIS Searches the textual output of a command for a pattern. .EXAMPLE Get-Service | Select-TextOutput audio Finds all references to "Audio" in the output of Get-Service #> param( ## The pattern to search for $Pattern ) Set-StrictMode -Version Latest $input | Out-String -Stream | Select-String $pattern
For more information about running scripts, see Run Programs, Scripts, and Existing Tools.
The Out-GridView
cmdlet is
one of the rare PowerShell cmdlets that displays a graphical user
interface. While the Where-Object
and
Sort-Object
cmdlets are the most common way to sort
and filter lists of items, the Out-GridView
cmdlet is
very effective at the style of repeated refinement that sometimes helps
you develop complex queries. Figure 1-3 shows the
Out-GridView
cmdlet in action.
Out-GridView
lets you
primarily filter your command output in two ways: a quick
filter expression and a criteria
filter.
Quick filters are fairly simple. As
you type text in the topmost “Filter” window,
Out-GridView
filters the list to contain only items
that match that text. If you want to restrict this text filtering to
specific columns, simply provide a column name before your search string
and separate the two with a colon. You can provide multiple search
strings, in which case Out-GridView
returns only rows
that match all of the required strings.
Note
Unlike most filtering cmdlets in PowerShell,
the quick filters in the Out-GridView
cmdlet do not
support wildcards or regular expressions. For this type of advanced
query, criteria-based filtering can help.
Criteria filters give fine-grained
control over the filtering used by the Out-GridView
cmdlet. To apply a criteria filter, click the “Add criteria” button and
select a property to filter on. Out-GridView
adds a
row below the quick filter field and lets you pick one of several
operations to apply to this property:
In addition to these filtering options,
Out-GridView
also lets you click and rearrange the
header columns to sort by them.
Once you’ve sliced and diced your command output, you
can select any rows you want to keep and press Ctrl-C to copy them to
the clipboard. Out-GridView
copies the items to the
clipboard as tab-separated data, so you can easily paste the
information into a spreadsheet or other file for further
processing.
Despite its clipboard output, exporting
items to the Out-GridView
cmdlet is primarily a
one-way operation. While you can process items somewhat further by
exporting them to a spreadsheet or text file, there is no way to
access the results of sorting and filtering as full-fidelity objects.
For an approach that supports this type of full-fidelity filtering,
see Program: Interactively Filter Lists of Objects.
Additionally, a graphical version is shown in Program: Add a Graphical User Interface to Your Script.
To redirect the output of a command into a
file, use either the Out-File
cmdlet or one
of the redirection operators.
Get-ChildItem | Out-File unicodeFile.txt Get-Contentfilename
.cs | Out-File -Encoding ASCIIfile.txt
Get-ChildItem | Out-File -Width 120 unicodeFile.cs
Get-ChildItem >files.txt
Get-ChildItem 2>errors.txt
The Out-File
cmdlet and redirection operators
share a lot in common. For the most part, you can use either. The
redirection operators are unique because they give the greatest amount
of control over redirecting individual streams. The Out-File
cmdlet is unique primarily because it
lets you easily configure the formatting width and encoding.
The default formatting width and the default output encoding are two aspects of output redirection that can sometimes cause difficulty.
The default formatting width sometimes causes
problems because redirecting PowerShell-formatted output into a file is
designed to mimic what you see on the screen. If your screen is 80
characters wide, the file will be 80 characters wide as well. Examples
of PowerShell-formatted output include directory listings (that are
implicitly formatted as a table) as well as any commands that you
explicitly format using one of the Format-*
set of
cmdlets. If this causes problems, you can customize the width
of the file with the -Width
parameter
on the Out-File
cmdlet.
The default output encoding sometimes causes
unexpected results because PowerShell creates all files using the UTF-16
Unicode encoding by default. This allows PowerShell to
fully support the entire range of international characters, cmdlets, and
output. Although this is a great improvement on traditional shells, it
may cause an unwanted surprise when running large search and replace
operations on ASCII source code files, for example. To force PowerShell
to send its output to a file in the ASCII encoding, use the -Encoding
parameter on the Out-File
cmdlet.
For more information about the Out-File
cmdlet, type Get-Help Out-File
. For a full list of
supported redirection operators, see Capturing Output.
You want to redirect the output of a pipeline into a file but add the information to the end of that file.
To redirect the output of a command into a
file, use either the -Append
parameter of the Out-File
cmdlet or
one of the appending redirection operators described in Capturing Output. Both support options to append text to
the end of a file.
Get-ChildItem | Out-File -Append files.txt
Get-ChildItem >> files.txt
The Out-File
cmdlet and redirection operators
share a lot in common. For the most part, you can use either. See the
discussion in Store the Output of a Command into a File for a more
detailed comparison of the two approaches, including reasons that you
would pick one over the other.
To record a transcript of your shell session,
run the command Start-Transcript
. It
has an optional -Path
parameter that defaults to a
filename based on the current system time. By default, PowerShell places
this file in the My Documents directory. To stop
recording the transcript of your shell system, run the command Stop-Transcript
.
Although the Get-History
cmdlet is helpful, it does not
record the output produced during your PowerShell session. To accomplish
that, use the Start-Transcript
cmdlet. In addition to the Path
parameter described
previously, the Start-Transcript
cmdlet also supports parameters that let you control how PowerShell
interacts with the output file.
Use the Import-Module
command to import third-party commands into your PowerShell
session.
To import a registered module by name:
Import-Module Name
To import a module from a specific directory:
Import-Module c:\path\to\module
To import a module from a specific file (module, script, or assembly):
Import-Module c:\path\to\module\file.ext
PowerShell supports two sets of commands that
enable additional cmdlets and providers: *-Module
and
*-PsSnapin
. Snapins were the packages for extensions
in version one of PowerShell. They supported only compiled extensions
and had onerous installation requirements.
Version two of PowerShell introduces modules that support everything that snapins support (and more) without the associated installation pain.
The most common way to import a module is by
name. PowerShell searches through every directory listed in the
PSModulePath
environment variable, looking for
subdirectories that match the name you specify. Inside those
directories, it looks for the module (*.psd1
,
*.psm1
, and *.dll
) with the same
name and loads it.
Note
When you install a module on your own system, the most common place to put it is in the WindowsPowerShell\Modules directory in your My Documents directory.
To have PowerShell look in another directory for modules, add
it to your personal PSModulePath
environment
variable, just as you would add a Tools directory
to your personal Path.
If you want to load a module from a directory
not in PSModulePath
, you can provide the entire
directory name and module name to the Import-Module
command. For example, for a module named Test
, use
Import-Module
c:\path\to\
.
As with loading modules by name, PowerShell looks in
Test
c:\temp\path\to
for a module
(*.psd1
, *.psm1
, or
*.dll
) named Test
and loads
it.
If you know the specific module file you want to load, you can also specify the full path to that module.
One popular source of additional commands is the PowerShell Community Extensions project, located at http://www.codeplex.com/PowerShellCX.
If you want to import these commands for every
PowerShell session, add a call to Import-Module
to
your PowerShell Profile.
You want to use the commands from a PowerShell-based product that launches a customized version of the PowerShell console, but in a regular PowerShell session.
Launch the customized version of the
PowerShell console, and then use the Get-
Module
and
Get-PsSnapin
commands to see what additional modules
and/or snapins it loaded.
As described in Extend Your Shell with Additional Commands, PowerShell
modules and snapins are the two ways that third parties can distribute
and add additional PowerShell commands. Products that provide customized
versions of the PowerShell console do this by calling
PowerShell.exe
with one of three parameters:
Regardless of which one it used, you can examine the resulting set of loaded extensions to see which ones you can import into your other PowerShell sessions.
The Get-PsSnapin
command
returns all snapins loaded in the current session. It always returns
the set of core PowerShell snapins, but it will also return any
additional snapins loaded by the customized environment. For example,
if the name of a snapin you recognize is
Product.Feature.Commands
, you can load that
into future PowerShell sessions by typing Add-PsSnapin
Product.Feature.Commands
. To
automate this, add the command into your PowerShell profile.
If you are uncertain of which snapin to
load, you can also use the Get-Command
command to
discover which snapin defines a specific command:
PS > Get-Command Get-Counter | Select PsSnapin PSSnapIn -------- Microsoft.PowerShell.Diagnostics
Like the Get-PsSnapin
command, the Get-Module
command returns all modules
loaded in the current session. It returns any modules you’ve added so
far into that session, but it will also return any additional modules
loaded by the customized environment. For example, if the name of a
module you recognize is ProductModule
, you
can load that into future PowerShell sessions by typing
Import-Module
ProductModule
. To automate this,
add the command into your PowerShell profile.
If you are uncertain of which module to
load, you can also use the Get-Command
command to
discover which module defines a specific command:
PS > Get-Command Start-BitsTransfer | Select Module Module ------ BitsTransfer
Subscribe to the
PowerShell.Exiting
engine event to have PowerShell
invoke a script or script block that saves any state you need.
To have PowerShell save your command history,
place a call to Enable-
History
Persistence
in your profile, as in
Example 1-14.
Example 1-14. Enable-HistoryPersistence.ps1
############################################################################## ## ## Enable-HistoryPersistence ## ## From Windows PowerShell Cookbook (O'Reilly) ## by Lee Holmes (http://www.leeholmes.com/guide) ## ############################################################################## <# .SYNOPSIS Reloads any previously saved command history and registers for the PowerShell.Exiting engine event to save new history when the shell exits. #> Set-StrictMode -Version Latest ## Load our previous history $GLOBAL:maximumHistoryCount = 32767 $historyFile = (Join-Path (Split-Path $profile) "commandHistory.clixml") if(Test-Path $historyFile) { Import-CliXml $historyFile | Add-History } ## Register for the engine shutdown event $null = Register-EngineEvent -SourceIdentifier ` ([System.Management.Automation.PsEngineEvent]::Exiting) -Action { ## Save our history $historyFile = (Join-Path (Split-Path $profile) "commandHistory.clixml") $maximumHistoryCount = 1kb ## Get the previous history items $oldEntries = @() if(Test-Path $historyFile) { $oldEntries = Import-CliXml $historyFile -ErrorAction SilentlyContinue } ## And merge them with our changes $currentEntries = Get-History -Count $maximumHistoryCount $additions = Compare-Object $oldEntries $currentEntries ` -Property CommandLine | Where-Object { $_.SideIndicator -eq "=>" } | Foreach-Object { $_.CommandLine } $newEntries = $currentEntries | ? { $additions -contains $_.CommandLine } ## Keep only unique command lines. First sort by CommandLine in ## descending order (so that we keep the newest entries,) and then ## re-sort by StartExecutionTime. $history = @($oldEntries + $newEntries) | Sort -Unique -Descending CommandLine | Sort StartExecutionTime ## Finally, keep the last 100 Remove-Item $historyFile $history | Select -Last 100 | Export-CliXml $historyFile }
PowerShell provides easy script-based access
to a broad variety of system, engine, and other events. You can register
for notification of these events and even automatically process any of
those events. In this example, we subscribe to the only one currently
available, which is called PowerShell.Exiting
.
PowerShell generates this event when you close a session.
For PowerShell to handle this event, you must use the
exit
keyword to close your session, rather than the X
button at the top right of the console window. In the Integrated
Scripting Environment, the close button generates this event as
well.
This script could do anything, but in this example we have it save our command history and restore it when we launch PowerShell. Why would we want to do this? Well, with a rich history buffer, we can more easily find and reuse commands we’ve previously run. For two examples of doing this, see Recipes and .
Example 1-14 takes two main actions.
First, we load our stored command history (if any exists). Then, we
register an automatic action to be processed whenever the engine
generates its PowerShell.Exiting
event. The action
itself is relatively straightforward, although exporting our new history
does take a little finesse. If you have several sessions open at the
same time, each will update the saved history file when it exits. Since
we don’t want to overwrite the history saved by the other shells, we
first reload the history from disk and combine it with the history from
the current shell.
Once we have the combined list of command lines, we sort them and pick out the unique ones before storing them back in the file.
For more information about working with PowerShell engine events, see Create and Respond to Custom Events.
Get Windows PowerShell Cookbook, 2nd Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.