100% found this document useful (4 votes)
2K views567 pages

Mastering PowerShell

Free Powershell 1.0 ebook

Uploaded by

ttravel2552
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (4 votes)
2K views567 pages

Mastering PowerShell

Free Powershell 1.0 ebook

Uploaded by

ttravel2552
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Copyright

Copyright
2009 BBS Technologies
ALL RIGHTS RESERVED. No part of this work covered by the copyright herein may be reproduced,
transmitted, stored, or used in any form or by any means graphic, electronic, or mechanical,
including but not limited to photocopying, recording, scanning, digitizing, taping, Web distribution,
information networks, or information storage and retrieval systems except as permitted under
Section 107 or 108 of the 1976 United States Copyright Act without the prior written permission of
the publisher.
For permission to use material from the text please contact Idera at [email protected].
Microsoft Windows PowerShell and Microsoft SQL Server are registered trademarks of
Microsoft Corporation in the United Stated and other countries. All other trademarks are the
property of their respective owners.
About the Author
About the Author
Dr. Tobias Weltner is one of the most visible PowerShell MVPs in Europe. He has
published more than 80 books on Windows and Scripting Techniques with Microsoft
Press and other publishers, is a regular speaker at conferences and road shows and
does high level PowerShell and Scripting trainings for companies throughout
Europe. He created the powershell.com website and community in an effort to help
people adopt and use PowerShell more efficiently. As software architect, he created
a number of award-winning scripting tools such as SystemScripter (VBScript), the
original PowerShell IDE and PowerShell Plus, a comprehensive integrated
PowerShell development system.
Acknowledgments
Acknowledgments
First and foremost, Id like to thank my family who is always a source of inspiration
and encouragement. A special thanks to Idera, Rick Pleczko, David Fargo, Richard
Giles, Conley Smith and David Twamley for helping to bring this book to the English
speaking world. It has been a pleasure working with you all. You are all great
friends and partners. For Aleksandar Nikolic, our technical editor and very good
friend, I thank you very much for all your hard work. You not only edited the book,
but many times your insight and attention to detail improved the overall value as
well. Finally, I'd like to thank my dog and close companion Cofi for his loyal faith
and for pulling me away from work from time to time to enjoy life.
Sincerely,
Dr. Tobias Weltner
Windows PowerShell MVP
Mastering PowerShell
Mastering PowerShell
Chapters
1. The PowerShell Console 11. Finding and Avoiding Errors
2. Interactive PowerShell 12. Command Discovery and Scriptblocks
3. Variables 13. Text and Regular Expressions
4. Arrays and Hashtables 14. XML
5. The PowerShell Pipeline 15. The File System
6. Using Objects 16. The Registry
7. Conditions 17. Processes, Services, Event Logs
8. Loops 18. Windows Management Instrumentation
9. Functions 19. User Management
10. Scripts 20. Your Own Cmdlets and Extensions
Extras
Copyright Notice About the Author
Acknowledgments About Idera's PowerShell Plus
Sponsors Additional Resources
CHAPTER 1.
The PowerShell Console
The PowerShell Console
Welcome to PowerShell! This chapter will introduce you to the PowerShell console and show you how
to configure it, including font colors and sizes, editing and display options.
Topics Covered:
Starting PowerShell
Figure 1.1: How to always open PowerShell with administrator rights
First Steps with the Console
Figure 1.2: First commands in the PowerShell console
Incomplete and Multi-line Entries
Important Keyboard Shortcuts
Deleting Incorrect Entries
Overtype Mode
Command History: Reusing Entered Commands
Automatically Completing Input
Scrolling Console Contents
Selecting and Inserting Text
QuickEdit Mode
Figure 1.3: Marking and copying text areas in QuickEdit mode
Standard Mode
Customizing the Console
Opening Console Properties
Figure 1.4: Opening console properties
Defining Options
Figure 1.5: Defining the QuickEdit and Insert modes
Specifying Fonts and Font Sizes
Figure 1.6: Specifying new fonts and font sizes
Setting Window and Buffer Size
Figure 1.7: Specifying the size of the window buffer
Selecting Colors
Figure 1.8: Select better colors for your console
Directly Assigning Modifications in PowerShell
Saving Changes
Piping and Routing
Piping: Outputting Information Page by Page
Redirecting: Storing Information in Files
Summary
Table 1.1: Important keys and their meaning in the PowerShell console
Table of Contents | About PowerShell Plus 6 Sponsors | Resources | BBS Technologies
Starting PowerShell
After you installed PowerShell, you'll find the PowerShell icon on the Start Menu in the program
folder Windows PowerShell. Open this program folder and click on Windows PowerShell and the
PowerShell console comes up. By the way, if you aren't able to find the program folder, PowerShell
is probably not installed on your computer. It is an optional download from Microsoft for Windows
XP, Server 2003, and Windows Vista.
You can also start PowerShell directly. Just press (Windows)+(R) to open the Run window and then
enter powershell (Enter). If you use PowerShell often, you should open the program folder for
Windows PowerShell and right-click on Windows PowerShell. That will give you several options:
Add to the start menu: On the context menu, click on Pin to Start Menu so that PowerShell
will be displayed directly on your start menu from now on and you won't need to open its
program folder first.
Quick Launch toolbar: Click Add to Quick Launch toolbar if you use Windows Vista and
would like to see PowerShell right on the Quick Launch toolbar inside your taskbar. Windows
XP lacks this command so XP users will have to add PowerShell to the Quick Launch toolbar
manually.
e!board shortcuts: Administrators particularly prefer using a keyboard instead of a
mouse. If you select Properties on the context menu, you can specify a key combination in
the hot-key field. Just click on this field and press the key combination intended to start
PowerShell, such as (Alt)+(P). In the properties window, you also have the option of setting
the default window size to start PowerShell in a normal, minimized, or maximized window.
Autostart: If you use PowerShell daily, it makes sense to Autostart the application. Then, it
will automatically launch the PowerShell window when Windows starts up so all you have to
do to bring it up is to click on its window button on the taskbar. If you want to set up a
PowerShell autostart, open the Windows PowerShell 1.0 program folder and right-click on
Windows PowerShell on the All Prora!s menu of your start menu. On the context menu,
select "opy. Next, open Startup folder, right-click on a blank area, and select paste on the
context menu. This will place a PowerShell shortcut in the folder. All you have to do now is
click on the shortcut with the right button of your mouse and choose Properties. Specify the
window size as Mini!i#ed.
Here is a special note for Vista users: The default settings of
Windows Vista start all programs without administrator privileges.
This applies to the PowerShell console as well. So, even if you have
administrator privileges, you will initially have no access to your
administrator rights when using the PowerShell console. That's a
new security feature that does make sense. You'd be surprised to see how
many routine tasks can be performed without these powerful (and potentially
dangerous) rights.
If you need more privileges because commands aren't working right or your
system complains about lacking administrator rights, then request your full
administrator privileges. To do so, right-click on the PowerShell entry on your
start menu and select Run As Ad!inistrator on the context menu. The
PowerShell console window won't show by default whether you have enabled
full administrator privileges, but you can add that feature later (see Chapter
9).
Table of Contents | About PowerShell Plus 7 Sponsors | Resources | BBS Technologies
If you'd like to always launch PowerShell with full administrator privileges,
click on the shortcut on the start menu with the right button of your mouse
and choose Properties. Then click below right on Ad$anced and activate the
option Run As Ad!inistrator.

"igure #$#: How to always open PowerShell with administrator rights
%Run without ad!inistrati$e pri$ilees whene$er possible&
"irst Steps with the Console
After PowerShell starts, its console window opens, and you see a blinking text prompt, asking for
your input with no icons or menus. PowerShell is a command console and almost entirely operated
via keyboard input. The prompt begins with "PS" and after it is the path name of the directory where
you are located. Start by trying out a few commands. For example, type:
hello (Enter)
As soon as you press (Enter), your entry will be sent to PowerShell. Because PowerShell has never
heard of the command "hello" you will be confronted with an error message highlighted in red.
Table of Contents | About PowerShell Plus 8 Sponsors | Resources | BBS Technologies

"igure #$%: First commands in the PowerShell console
For example, if you'd like to see which files and folders are in your current directory, then type dir
(Enter). You'll get a text listing of all the files in the directory. PowerShell's communication with you
is always text-based. PowerShell can do much more than display simple directory lists. Just pick a
different command as the next one provides a list of all running processes:
'et-Process (Enter)
PowerShell's advantage is its tremendous flexibility since it allows you to control and display nearly
all the information and operations on your computer. The command cls deletes the contents of the
console window and the e(it command ends PowerShell.
&ncomplete and 'ulti(line Entries
Whenever you enter something PowerShell cannot understand, you get a red error message,
explaining what went wrong. However, if you enter something that isn't wrong but incomplete (like a
string with one missing closing quote), PowerShell gives you a chance to complete your input. You
then see a double-prompt (">>"), and once you completed the line and pressed ENTER twice,
PowerShell executes the command. You can also bail out at any time and cancel the current
command or input by pressing: (Ctrl)+(C).
The "incomplete input" prompt will also appear when you enter an incomplete arithmetic problem
like this one:
2 + (Enter)
>> 6 (Enter)
>> (Enter)
8
Table of Contents | About PowerShell Plus 9 Sponsors | Resources | BBS Technologies
This feature enables you to make multi-line PowerShell entries:
"This is my little multiline entry.(Enter)
>> I'm now writing a text of several lines. (Enter)
>> And I'll keep on writing until it's no longer fun."(Enter)
>>(Enter)
This is my little multiline entry.
I'm now writing a text of several lines.
And I'll keep on writing until it's no longer fun.
The continuation prompt generally takes its cue from initial and terminal characters like open and
closed brackets or quotation marks at both ends of a string. As long as the symmetry of these
characters is incorrect, you'll continue to see the prompt. However, you can activate it even in other
cases:
dir `(Enter)
>> -recurse(Enter)
>>(Enter)
So, if the last character of a line is what is called a "backtick" character, the line will be continued.
You can retrieve that special character by pressing (`).
&mportant e!board Shortcuts
Shortcuts are important since almost everything in PowerShell is keyboard-based. For example, by
pressing the keys (Arrow left) and (Arrow right), you can move the blinking cursor to the left or
right. Use it to go back and correct a typo. If you want to move the cursor word by word, hold down
(Ctrl) while pressing the arrow keys. To place the cursor at the beginning of a line, hit (Home).
Pressing (End) will send the cursor to the end of a line.
If you haven't entered anything, then the cursor won't move since
it will only move within entered text. There's one exception: if
you've already entered a line and pressed (Enter) to execute the
line, you can make this line appear again character-by-character
by pressing (Arrow right).
)eleting &ncorrect Entries
If you've mistyped something, press (Backspace) to delete the character to the left of the blinking
cursor. (Del) erases the character to the right of the cursor. And you can use (Esc) to delete your
entire current line.
The hotkey (Ctrl)+(Home) works more selectively: it deletes all the characters at the current
position up to the beginning of the line. Characters to the right of the current position (if there are
any) remain intact. (Ctrl)+(End) does it the other way around and deletes everything from the
Table of Contents | About PowerShell Plus 10 Sponsors | Resources | BBS Technologies
current position up to the end of the line. Both combinations are useful only after you've pressed
(Arrow left) to move the cursor to the middle of a line, specifically when text is both to the left and
to the right of the cursor.
*vert!pe 'ode
If you enter new characters and they overwrite existing characters, then you know you are in type-
over mode. By pressing (Insert) you can switch between insert and type-over modes. The default
input mode depends on the console settings you select. You'll learn more about console settings
soon.
Command +istor!: ,eusing Entered Commands
For example, you don't have to re-type commands to edit them. Simply press (Arrow up) to re-
display the command that you entered. Press (Arrow up) and (Arrow down) to scroll up and down
your command history. Using (F5) and (F8) do the same as the up and down arrow keys.
This command history feature is extremely useful. Later, you'll learn how to configure the number of
commands the console "remembers." The default setting is the last 50 commands. You can display
all the commands in your history by pressing (F7) and then scrolling up and down the list to select
commands using (Arrow up) and (Arrow down) and (Enter).
The numbers before the commands in the Command History list
only denote the sequence number. You cannot enter a number to
select the associated command. What you can do is move up and
down the list by hitting the arrow keys.
Simply press (F9) to 'activate' the numbers so that you can select a
command by its number. This opens a menu that accepts the numbers and
returns the desired command.
The keyboard sequence (Alt)+(F7) will clear the command history and start
you off with a new list.
(F8) provides more functionality than (Arrow up) as it doesn't just show the last command you
entered, but keeps a record of the characters you've already typed in. If, for example, you'd like to
see all the commands you've entered that begin with "d", type:
d(F8)
Press (F8) several times. Every time you press a key another command will be displayed from the
command history provided that you've already typed in commands with an initial "d."
Table of Contents | About PowerShell Plus 11 Sponsors | Resources | BBS Technologies
Automaticall! Completing &nput
An especially important key is (Tab). It will save you a great deal of typing (and typing errors).
When you press this key, PowerShell will attempt to complete your input automatically. For example,
type:
cd(Tab)
The command cd changes the directory in which you are currently working. Put at least one space
behind the command and then press (Tab). PowerShell suggests a subdirectory. Press (Tab) again to
see other suggestions. If (Tab) doesn't come up with any suggestions, then there probably aren't
any subdirectories available.
This feature is called AutoComplete, which works in many places. For example, you just learned how
to use the command 'et-Process, which lists all running processes. If you want to know what other
commands there are that begin with "Get-", then type:
Get-(Tab)
Just make sure that there's no space before the cursor when you press (Tab). Keep hitting (Tab) to
see all the commands that begin with "Get-".
A more complete review of the AutoComplete feature is available in
Chapter 9.
AutoComplete works really well with long path names that require a lot of typing. For example:
c:\p(Tab)
Every time you press (Tab), PowerShell will prompt you with a new directory or a new file that
begins with "c:\p." So, the more characters you type, the fewer options there will be. In practice,
you should type in at least four or five characters to reduce the number of suggestions.
When the list of suggestions is long, it can take a second or two until PowerShell has compiled all the
possible suggestions and displays the first one.
Wildcards are allowed in path names. For example, if you enter c)*
pr+e (Tab) in a typical Windows system, PowerShell will respond
with "c:\Program Files".
PowerShell will automatically put the entire response inside double quotation
marks if the response contains whitespace characters.
Table of Contents | About PowerShell Plus 12 Sponsors | Resources | BBS Technologies
Scrolling Console Contents
The visible part of your console depends on the size of your console window, which you can change
with your mouse. Drag the window border while holding down your left mouse button until the
window is the size you want. Note that the actual contents of the console, the "screen buffer," don't
change. So, if the window is too small to show everything, you should use the scroll bars.
Selecting and &nserting Text
Use your mouse if you'd like to select text inside the PowerShell window and copy it onto the
clipboard. Move the mouse pointer to the beginning of the selected text, hold down the left mouse
button and drag it over the text area that you want to select.
QuickEdit 'ode
QuickEdit is the default mode for selecting and copying text in PowerShell. Select the text using your
mouse and PowerShell will highlight it. After you've selected the text, press (Enter) or right-click on
the marked area. This will copy the selected text to the clipboard. which you can now paste into
other applications. To unselect press (Esc).
You can also insert the text in your console at the blinking command line by right-clicking your
mouse.

"igure #$-: Marking and copying text areas in QuickEdit mode
Table of Contents | About PowerShell Plus 13 Sponsors | Resources | BBS Technologies
Standard 'ode
If QuickEdit is turned off and you are in Standard mode, the simplest way to mark and copy text is
to right-click in the console window. If QuickEdit is turned off, a context menu will open.
Select Mark to mark text and Paste if you want to insert the marked text (or other text contents that
you've copied to the clipboard) in the console.
It's usually more practical to activate QuickEdit mode so that you won't have to use to the context
menu.
Customi.ing the Console
You can customize a variety of settings in the console including edit mode, screen buffer size, font
colors, font sizes etc.
*pening Console Properties
The basic settings of your PowerShell console are configured in a special Properties dialog box. Click
on the PowerShell icon on the far left of the title bar of the console window to open it.

"igure #$/: Opening console properties
That will open a context menu. You should select Properties and A dialog box will open.
To get help, click on the question mark button on the title bar of the window. A question mark is
then pinned to your mouse pointer. Next, click on the option you need help for. The help appears as
a ScreenTip window.
Table of Contents | About PowerShell Plus 14 Sponsors | Resources | BBS Technologies
)e0ining *ptions
Under the heading ,ptions are four panels of options:

"igure #$1: Defining the QuickEdit and Insert modes
Edit options: You should select the QuickEdit mode as well as the insert mode. We've
already discussed the advantages of the Quick-dit !ode: it makes it much easier to select,
copy, and insert text. The insert !ode makes sure that new characters don't overwrite
existing input so new characters will be added without erasing text you've already typed in
when you're editing command lines.
Cursor si.e: +ere is where you specify the size of the blinking cursor.
)ispla! options: Determine whether the console should be displayed as a window or full
screen. The "window" option is best so that you can switch to other windows when you're
working. The full screen display option is not available on all operating systems.
Command histor!: Here you can choose how many command inputs the console
"remembers". This allows you to select a command from the list by pressing (Arrow up) or
(F7). The option .iscard ,ld .uplicates ensures that the list doesn't have any duplicate
entries. So, if you enter one command twice, it will appear only once in the history list.
Speci0!ing "onts and "ont Si.es
On the /ont tab, you can choose both the font and the font size displayed in the console.
The console often uses the raster font as its default. This font is available in a specific range of sizes
with available sizes shown in the "Size" list. Scalable TrueType fonts are much more flexible. They're
marked in the list by a "TT" symbol. When you select a TrueType font, you can choose any size in
the size list or enter them as text in the text box. TrueType fonts can be dynamically scaled.
Table of Contents | About PowerShell Plus 15 Sponsors | Resources | BBS Technologies

"igure #$2: Specifying new fonts and font sizes
You should also try experimenting with TrueType fonts by using the "bold fonts" option. TrueType
fonts are often more readable if they're displayed in bold.
Your choice of fonts may at first seem a bit limited. To get more
font choices, you can add them to the console font list. The limited
default font list is supposed to prevent you from choosing
unsuitable fonts for your console.
One reason for this is that the console always uses the same width for each
character (fixed width fonts). This restricts the use of most Windows fonts
because they're proportional typefaces: every character has its own width.
For example, an "" is narrower than an "m". If you're sure that a certain font
will work in the console, then here's how to add the font to the console font
list.
Open your registry editor. In the key
01-23L,"AL3MA"045-*S,/6WAR-*Microso7t*Windows 56*
"urrent8ersion*"onsole*6rue6ype/ont insert a new "string value" and give
this entry the name "00" (numbers, not letters).
If there's already an entry that has this name, then call the new entry "000"
or add as many zeroes as required to avoid conflicts with existing entries.
You should then double-click your new entry to open it and enter the name of
the font. The name must be exactly the same as the official font name, just
the way it's stated under the key
01-23L,"AL3MA"045-*S,/6WAR-*Microso7t*Windows
56*"urrent8ersion*/onts.
Table of Contents | About PowerShell Plus 16 Sponsors | Resources | BBS Technologies
The newly added font will now turn up in the console's option field. However,
the new font will work only after you either log off at least once or restart
your computer. If you fail to do so, the console will ignore your new font
when you select it in the dialog box.
Setting 3indow and 4u00er Si.e
On the Layout tab, you can specify how large the screen buffer should be, meaning how much
information the console should "remember" and how far back you can scroll with the scroll bars.
You should select a width of at least 120 characters in the window buffer size area with the height
should be at least 1,000 lines or larger. This gives you the opportunity to use the scroll bars to scroll
the window contents back up so that you can look at all the results of your previous commands.

"igure #$5: Specifying the size of the window buffer
You can also set the window size and position on this tab if you'd like your console to open at a
certain size and screen position on your display. Choose the option Let syste! position window and
Windows will automatically determine at what location the console window will open.
Selecting Colors
On the "olors tab, you can select your own colors for four areas:
Table of Contents | About PowerShell Plus 17 Sponsors | Resources | BBS Technologies
Screen text: Console font
Screen background: Console background color
Popup text: Popup window font, such as command history's (F7)
Popup background: Popup window background color
You have a palette of 16 colors for these four areas. So, if you want to specify a new font color, you
should first select the option Screen 6e(t and click on one of the 16 colors. If you don't like any of
the 16 colors, then you can mix your own special shade of color. Just click on a palette color and
choose your desired color value at the upper right from the primary colors red, green, and blue.

"igure #$6: Select better colors for your console
)irectl! Assigning 'odi0ications in PowerShell
Some of the console configuration can also be done from within PowerShell code. You'll hear more
about this later. To give you a quick impression, take a look at this:
$host.ui.rawui (Enter)
$host.ui.rawui.ForegroundColor = "Yellow" (Enter)
$host.ui.rawui.WindowTitle = "My Console" (Enter)
These changes will only be temporary. Once you close and re-open PowerShell, the changes are
gone. You would have to include these lines into one of your "profile scripts," which run every time
you launch PowerShell, to make them permanent. You can read more about this in Chapter 10.
Table of Contents | About PowerShell Plus 18 Sponsors | Resources | BBS Technologies
Saving Changes
Once you've successfully specified all your settings in the dialog box, you can close the dialog box. If
you're using Windows Vista, all changes will be saved immediately, and when you start PowerShell
the next time, your new settings will already be in effect. You may need Admin rights to save
settings if you launched PowerShell with a link in your start menu that applies for all users.
If you're using Windows XP, you'll see an additional window and a message asking you whether you
want to save changes temporarily (Apply properties to current window only) or permanently (Modify
shortcut that started this window).
Piping and ,outing
You may want to view the information page by page or save it in a file since some commands output
a lot of information.
Piping: *utputting &n0ormation Page b! Page
The pipe command !ore outputs information screen page by screen page. You will need to press a
button (like Space) to continue to the next page.
Piping uses the vertical bar (|). The results of the command to the left of the pipe symbol are then
fed into the command on the right side of the pipe symbol. This kind of piping is also known in
PowerShell as the "pipeline":
Get-Process | more (Enter)
You can press (Ctrl)+(C) to stop output. Piping also works with other commands, not just !ore. For
example, if you'd like to get a sorted directory listing, pipe the result to Sort-Object and specify the
columns you would like to sort:
dir | Sort-Object -property Length, Name (Enter)
You'll find more background information on piping as well as many useful examples in Chapter 5.
,edirecting: Storing &n0ormation in "iles
If you'd like to redirect the result of a command to a file, you can use the redirection symbol ">":
Help > help.txt (Enter)
The information won't appear in the console but will instead be redirected to the specified file. You
can then open the file.
However, opening a file in PowerShell is different from opening a file in the classic console:
Table of Contents | About PowerShell Plus 19 Sponsors | Resources | BBS Technologies
help.txt (Enter)
The term "help.txt" is not recognized as a cmdlet, function,
operable program, or script file. Verify the term and try again.
At line:1 character:8
help.txt !!!!
If you only specify the file name, PowerShell will look for it in all folders listed in the PATH
environment variable. So to open a file, you will have to specify its absolute or relative path name.
For example:
.\help.txt (Enter)
Or, to make it even simpler, you can use AutoComplete and hit (Tab) after the file name:
help.txt(Tab)
The file name will automatically be completed with the absolute path name, and then you can open
it by pressing (Enter):
& "C:\Users\UserA\help.txt" (Enter)
You can also append data to an existing file. For example, if you'd like to supplement the help
information in the file with help on native commands, you can attach this information to the existing
file with the redirection symbol ">>":
Cmd /c help >> help.txt (Enter)
If you'd like to directly process the result of a command, you won't need traditional redirection at all
because PowerShell can also store the result of any command to a variable:
$result = ing !".!".!".!"
$result
Reply from 10.10.10.10: bytes=32 time<1ms TTL=128
Reply from 10.10.10.10: bytes=32 time<1ms TTL=128
Reply from 10.10.10.10: bytes=32 time<1ms TTL=128
Reply from 10.10.10.10: bytes=32 time<1ms TTL=128
Ping statistics for 10.10.10.10:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 0ms, Average = 0ms
Variables are universal data storage and variable names always start with a "$". You'll find out more
about variables in Chapter 3.
Table of Contents | About PowerShell Plus 20 Sponsors | Resources | BBS Technologies
Summar!
PowerShell is an optional component for Windows XP and better. You will have to download and
install PowerShell before using it. Beginning with Windows Server 2008, PowerShell is included with
Windows by default. It still needs to be enabled in Windows software feature list. You will find
PowerShell, like any other program, in the start menu below "All Programs." It is located in the
program folder Windows PowerShell 1.0. The program file name is "powershell.exe."
PowerShell is a basic console program that relies heavily on text input. There are plenty of special
keys listed in Table 1.1.
e! 'eaning
(Alt)+(F7) Deletes the current command history
(PgUp), (PgDn)
Display the first (PgUp) or last (PgDn) command you used
in current session
(Enter) Send the entered lines to PowerShell for execution
(End) Moves the editing cursor to the end of the command line
(Del) Deletes the character to the right of the insertion point
(Esc) Deletes current command line
(F2)
Moves in current command line to the next character
corresponding to specified character
(F4)
Deletes all characters to the right of the insertion point up
to specified character
(F7) Displays last entered commands in a dialog box
(F8)
Displays commands from command history beginning with
the character that you already entered in the command
line
(F9) Opens a dialog box in which you can enter the number of
a command from your command history to return the
command. (F7) displays numbers of commands in
Table of Contents | About PowerShell Plus 21 Sponsors | Resources | BBS Technologies
command history
(Left arrow),
(Right arrow)
Move one character to the left or right respectively
(Arrow up), (Arrow
down), (F5), (F8)
Repeat the last previously entered command
(Home) Moves editing cursor to beginning of command line
(Backspace) Deletes character to the left of the insertion point
(Ctrl)+(C) Cancels command execution
(Ctrl)+(End)
Deletes all characters from current position to end of
command line
(Ctrl)+(Arrow
left), (Ctrl)+
(Arrow right)
Move insertion point one word to the left or right
respectively
(Ctrl)+(Home)
Deletes all characters of current position up to beginning
of command line
(Tab) Automatically completes current entry, if possible
Table #$#: Important keys and their meaning in the PowerShell console
You will find that the keys (Arrow up), which repeats the last command, and (Tab), which completes
the current entry, are particularly useful. By hitting (Enter), you complete an entry and send it to
PowerShell. If PowerShell can't understand a command, an error message appears highlighted in red
stating the possible reasons for the error. Two special commands are cls (deletes the contents of the
console) and e(it (ends PowerShell).
You can use your mouse to select information in the console and copy it to the Clipboard by pressing
(Enter) or by right-clicking when you have turned on the QuickEdit mode. With QuickEdit mode
turned off, you will have to right-click inside the console and then select Mark in a context menu.
The basic settings of the console-QuickEdit mode as well as colors, fonts, and font sizes-can be
customized in the properties window of the console. This can be accessed by right-clicking the icon
to the far left in the title bar of the console window. In the dialog box, select Properties.
Table of Contents | About PowerShell Plus 22 Sponsors | Resources | BBS Technologies
Along with the commands, a number of characters in the console have special meanings as you have
already become acquainted with three of them:
Piping: The vertical bar "|" symbol pipes the results of a command to the next. When you
pipe the results to the command !ore, the screen output will be paused once the screen is
full, and continued when you press a key.
,edirection: The symbol ">" redirects the results of a command to a file. You can then open
and view the file contents. The symbol ">>" appends information to an existing file.
Table of Contents | About PowerShell Plus 23 Sponsors | Resources | BBS Technologies
CHAPTER 2.
Interactive PowerShell
Interactive PowerShell
PowerShell has two faces: interactivity and script automation. In this chapter, you will first learn
how to work with PowerShell interactively. Then, we will take a look at PowerShell scripts.
Topics Covered:
PowerShell as a Calculator
Calculating with Number Systems and Units
Table 2.1: Arithmetic operators
Executing External Commands
Starting the "Old" Console
Discovering Useful Console Commands
Figure 2.1: Run PowerShell as administrator
Security Restrictions at Program Start
Trustworthy Subdirectories
Cmdlets: "Genuine" PowerShell Commands
Table 2.2: The most important standard actions and their descriptions
Using Parameters
Using Named Parameters
Switch Parameter
Positional Parameters
Common Parameters
Table 2.3: Common parameters in effect for (nearly) all cmdlets
Aliases: Giving Commands Other Names
Resolving Aliases
Creating Your Own Aliases
Removing-or Permanently Retaining-an Alias
Overwriting Alias Definitions and Deleting Them Manually
Functions: "Expanded" Aliases
Calling Commands with Arguments
Creating Shortcut Commands
Invoking Files and Scripts
Starting Scripts
Running Batch Files
Running VBScript Files
Running PowerShell Scripts
Summary
PowerShell as a Calculator
You can use the PowerShell console to execute arithmetic operations the same way you would with a
calculator. Just enter a math expression and PowerShell will give you the result:
2+# (Enter)
Table of Contents | About PowerShell Plus 24 Sponsors | Resources | BBS Technologies
6
You can use all of the usual basic arithmetic operations. Even parentheses will work just the way
they do when you use your pocket calculator:
$!2+%& * ' / #.% (Enter)
11.3333333333333
Parentheses play a special role in PowerShell as they always works
from the inside out: the results inside the parentheses are
produced before evaluating the expressions outside of the
parentheses, i.e. (2*2)*2 = 4*2. For example, operations
performed within parentheses have priority and ensure that
multiplication operations do not take precedence over addition operations.
Parentheses are also important when using PowerShell commands, as you'll
discover in upcoming chapters. For example, you could list the contents of
subdirectories with the dir command and then determine the number of files
in a folder by enclosing the dir command in parentheses.
$(ir *.txt&.Count (Enter)
12
Using a comma instead of a decimal point seems to return the wrong result:
#,' + 2 (Enter)
4
3
2
In the example above, PowerShell simply displayed the numbers again. The comma always creates
an array. The important thing to remember is that the decimal point is always a point and not a
comma in PowerShell.
Calculating with 7umber S!stems and 8nits
The next arithmetic problem is a little unusual.
#G) / *2"+) (Enter)
5.68888888888889
Table of Contents | About PowerShell Plus 25 Sponsors | Resources | BBS Technologies
The example above calculates how many CD-ROMs can be stored on a DVD. PowerShell supports
units like kilobyte, megabyte, and gigabyte. Just make sure you do not use a space between number
and unit.
!m, (Enter)
1048576
The units 19, M9, and '9 can be upper or lower case-how you
write them doesn't matter to PowerShell. However, white space
characters do matter. Units of measure must directly follow the
number and must not be separated from it by a space. Otherwise,
PowerShell will interpret the unit as a new command.
Take a look at the following command line:
!2 + "-./ (Enter)
187
PowerShell can easily understand hexadecimal values: simply prefix the number with "0x":
"-.//0 (Enter)
45054
Here is a quick summary:
*perators: Arithmetic problems can be solved with the help of operators. Operators evaluate
the two values to the left and the right. For basic operations, a total of five operators are
available, which are also called "arithmetic operators" (Table 2.1).
4rackets: Brackets group statements and ensure that expressions in parentheses are
evaluated first.
)ecimal point: Fractions use a point as decimal separator (never a comma).
Comma: Commas create arrays and so are irrelevant for normal arithmetic operations.
Special conversions: Hexadecimal numbers are designated by the prefix "0x", which
ensures that they are automatically converted into decimal values. If you add one of the KB,
MB, or GB units to a number, the number will be multiplied by the unit. White space
characters aren't allowed between numbers and values.
,esults and 0ormats: Numeric results are always returned as decimal values. If you'd like
to see the results presented in a different way, use a format operator like -7, which will be
discussed in detail later in this book.
*perator )escription example result
+ Adds two values 1 9 /$1 :$1
Table of Contents | About PowerShell Plus 26 Sponsors | Resources | BBS Technologies
%gb 9 #%;mb %%5--#%526
;x#;; 9 1 %2#
<+ello < 9
<there<
<+ello there<
- Subtracts two values 1 ( /$1 ;$1
#%gb ( /$1gb 6;1-;2-26;
%;; ( ;xA4 %:
* 'ultiplies two values 1 = /$1 %%$1
/mb = - #%16%:#%
#% = ;xC; %-;/
<x< = 1 <xxxxx<
/ )ivides two values 1 > /$1
#$#############
#
#mb > -;kb
-/$#-----------
-
;x""A4 > ;xC 1/1/?%1
%
Supplies the rest o0
division
1@/$1 ;$1
Table %$#: Arithmetic operators
Table of Contents | About PowerShell Plus 27 Sponsors | Resources | BBS Technologies
Executing External Commands
PowerShell can also launch external programs in very much the same way the classic console does.
For example, if you want to examine the settings of your network card, enter the command ipcon7i
-it works in PowerShell the same way it does in the traditional console:
1pcon2ig
Windows IP Configuration
Wireless LAN adapter Wireless Network Connection:

Connection-specific DNS Suffix:
Connection location IPv6 Address . : fe80::6093:8889:257e:8d1%8
IPv4 address . . . . . . . . . . : 192.168.1.35
Subnet Mask . . . . . . . . . . : 255.255.255.0
Standard Gateway . . . . . . . . . : 192.168.1.1
This following command enables you to verify if a Web site is online and tells you the route the data
packets are sent between a Web server and your computer:
3racert powershell.com
Trace route to powershell.com [74.208.54.218] over a maximum of 30 hops:
1 12 ms 7 ms 11 ms TobiasWeltner-PC [192.168.1.1]
2 15 ms 16 ms 16 ms dslb-088-070-064-001.pools.arcor-ip.net
3 15 ms 16 ms 16 ms han-145-254-11-105.arcor-ip.net
(...)
17 150 ms 151 ms 152 ms vl-987.gw-ps2.slr.lxa.oneandone.net
18 145 ms 145 ms 149 ms ratdog.info
You can execute any Windows programs. Just type notepad (Enter)
or e(plorer (Enter).
There's a difference between text-based commands like ipcon7i
and Windows programs like 5otepad. Text-based commands are executed
synchronously, and the console waits for the commands to complete.
Windows-based programs are executed asynchronously. Press (Ctrl)+(C) to
cancel a text-based command, which may take longer than expected and is
blocking the console.
To clear the console screen type cls (Enter).
Table of Contents | About PowerShell Plus 28 Sponsors | Resources | BBS Technologies
Starting the <*ld< Console
To temporarily switch back to the "old" console, simply enter c!d (Enter). Since the old console is
just another text-based command, you can easily launch it from within PowerShell. To leave the old
console, type e(it (Enter). Even PowerShell can be closed by entering e(it. Most text-based
commands use exit to quit. Occasionally, the command :uit is required in commands instead of e(it.
)iscovering 8se0ul Console Commands
The c!d command can be used for just one command when you specify the parameter ;c. This is
useful for invoking an old console command like help. This command has no external program that
you can access directly from PowerShell, it's only available inside the classic console. Using this
command will return a list of many other useful external console commands.
Cmd /c Help
For more information on a specific command, type HELP command-name
ASSOC Displays or modifies file extension associations.
AT Schedules commands and programs to run on a computer.
ATTRIB Displays or changes file attributes.
BREAK Sets or clears extended CTRL+C checking.
CACLS Displays or modifies access control lists (ACLs) of files.
CALL Calls one batch program from another.
CD Displays the name of or changes the current directory.
CHCP Displays or sets the active code page number.
CHDIR Displays the name of or changes the current directory.
CHKDSK Checks a disk and displays a status report.
CHKNTFS Displays or modifies the checking of disk at boot time.
CLS Clears the screen.
CMD Starts a new instance of the Windows command interpreter.
COLOR Sets the default console foreground and background colors.
COMP Compares the contents of two files or sets of files.
COMPACT Displays or alters the compression of files on NTFS
partitions.
CONVERT Converts FAT volumes to NTFS. You cannot convert the
current drive.
COPY Copies one or more files to another location.
DATE Displays or sets the date.
DEL Deletes one or more files.
DIR Displays a list of files and subdirectories in a directory.
DISKCOMP Compares the contents of two floppy disks.
DISKCOPY Copies the contents of one floppy disk to another.
DOSKEY Edits command lines, recalls Windows commands, and creates
macros.
ECHO Displays messages, or turns command echoing on or off.
ENDLOCAL Ends localization of environment changes in a batch file.
ERASE Deletes one or more files.
EXIT Quits the CMD.EXE program (command interpreter).
FC Compares two files or sets of files, and displays the
differences between them.
FIND Searches for a text string in a file or files.
Table of Contents | About PowerShell Plus 29 Sponsors | Resources | BBS Technologies
FINDSTR Searches for strings in files.
FOR Runs a specified command for each file in a set of files.
FORMAT Formats a disk for use with Windows.
FTYPE Displays or modifies file types used in file extension
associations.
GOTO Directs the Windows command interpreter to a labeled line
in a batch program.
GRAFTABL Enables Windows to display an extended character set in
graphics mode.
HELP Provides Help information for Windows commands.
IF Performs conditional processing in batch programs.
LABEL Creates, changes, or deletes the volume label of a disk.
MD Creates a directory.
MKDIR Creates a directory.
MODE Configures a system device.
MORE Displays output one screen at a time.
MOVE Moves one or more files from one directory to another
directory.
PATH Displays or sets a search path for executable files.
PAUSE Suspends processing of a batch file and displays a message.
POPD Restores the previous value of the current directory saved
by PUSHD.
PRINT Prints a text file.
PROMPT Changes the Windows command prompt.
PUSHD Saves the current directory then changes it.
RD Removes a directory.
RECOVER Recovers readable information from a bad or defective disk.
REM Records comments (remarks) in batch files or CONFIG.SYS.
REN Renames a file or files.
RENAME Renames a file or files.
REPLACE Replaces files.
RMDIR Removes a directory.
SET Displays, sets, or removes Windows environment variables.
SETLOCAL Begins localization of environment changes in a batch file.
SHIFT Shifts the position of replaceable parameters in batch
files.
SORT Sorts input.
START Starts a separate window to run a specified program or
command.
SUBST Associates a path with a drive letter.
TIME Displays or sets the system time.
TITLE Sets the window title for a CMD.EXE session.
TREE Graphically displays the directory structure of a drive or
path.
TYPE Displays the contents of a text file.
VER Displays the Windows version.
VERIFY Tells Windows whether to verify that your files are written
correctly to a disk.
VOL Displays a disk volume label and serial number.
XCOPY Copies files and directory trees.
You can use all of the above commands in your PowerShell console. To try this, pick some
commands from the list. For example:
Table of Contents | About PowerShell Plus 30 Sponsors | Resources | BBS Technologies
Cmd /c help 4ol
As an added safety net, you should run PowerShell without
administrator privileges when experimenting with new commands.
That will protect you against mistakes, because most dangerous
commands can no longer be executed without administrator rights:
de2rag c:
"ou must ha#e Administrator pri#ileges to defragment a
#olume.
$se an administrator command line and then run the program
again.
If you must use admin privileges and have enabled User Account Control on
Windows Vista, remember to start your PowerShell explicitly with
administrator rights. To do this, right-click PowerShell.e(e and in the context
menu, select Run as Ad!inistrator.

"igure %$#: Run PowerShell as administrator.
%Run without ad!inistrator pri$ilees whene$er possible&
Table of Contents | About PowerShell Plus 31 Sponsors | Resources | BBS Technologies
Securit! ,estrictions at Program Start
Strangely enough, it seems that some programs can't be launched from PowerShell. While you can
launch notepad, you cannot launch wordpad:
5ordpad
The term "%ordpad" is not recognized as a cmdlet, function,
operable program or script file. Verify the term and try again.
At line:1 char:&
%ordpad !!!!
PowerShell always needs to know where the program is stored. So, if you know the exact path name
of Wordpad, PowerShell launches Wordpad after all - almost:
C:\program6\7indo56 N3\acce66orie6\wordpad.exe
The term "':(programs()indo%s" is not recognized as a
cmdlet, function, operable program or script file.
Verify the term and try again.
At line:1 char:*1
':(programs()indo%s !!!! +T(accessories(%ordpad.exe
Because the path name includes white space characters and because PowerShell interprets white
space characters as separators, PowerShell is actually trying to start the program
")*prora!s*Windows. An error message is generated because this path doesn't exist. If path
names include spaces, the path must be enclosed in quotation marks. But that causes another
problem:
"C:\programs\Windows NT\accessories\wordpad.exe"
C:\programs\Windows NT\accessories\wordpad.exe
PowerShell treats text in quotation marks as a string and immediately returns this string. To ensure
that PowerShell executes the text in the quotation marks, type an ampersand in front of it:
& "C:\programs\Windows NT\accessories\wordpad.exe"
Finally, WordPad successfully starts. Hmmm. Wouldn't it be easier to switch from the current
subdirectory to the subdirectory where the program we're looking for is located?
Cd "C:\programs\Windows NT\accessories"
wordpad.exe
The term "%ordpad" is not recognized as a cmdlet,
function, operable program or script file.
Verify the term and try again.
At line:1 char:11
%ordpad.exe !!!!
Table of Contents | About PowerShell Plus 32 Sponsors | Resources | BBS Technologies
This results in another error because PowerShell requires a relative or absolute path. The absolute
path name is the complete path while a relative path name always refers to the current directory. To
launch a program in your current subdirectory, you use this relative path:
.\wordpad.exe
Trustworth! Subdirectories
PowerShell distinguishes between trustworthy folders and all other folders. You won't need to
provide the path name or append the file extension to the command name if the program is located
in a trustworthy folder. Commands like pin or ipcon7i work as-is because they are in located a
trustworthy folder, while the program in our last example, WordPad, is not.
The Windows environment variable Path determines whether a folder is trustworthy or not. All
folders listed in this environment variable are treated as "trustworthy" by PowerShell. You could put
all your important programs in one of the folders listed in the environment variable Path. You can
find out this list by entering:
$env:Path
C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\program
Files\Softex\OmniPass;C:\Windows\System32\WindowsPowerShell\v1.0\;c
:\program Files\Microsoft SQL Server\90\Tools\binn\;C:\program File
s\ATI Technologies\ATI.ACE\Core-Static;C:\program Files\MakeMsi\;C:
\program Files\QuickTime\QTSystem\
You'll find more on variables as well as special environment
variables in the next chapter.
As a clever alternative, you can add other folders containing important programs to your Path
environment variables, such as:
$env:path += ";C:\programs\Windows NT\accessories"
wordpad.exe
After this change, you can suddenly launch WordPad just by entering its program name. Note that
your change to the environment variable Path is valid only as long as PowerShell is running. Once
you end PowerShell, your modification is discarded. So, if you'd like to permanently extend Path,
you need to add the line for the extension to one of your profile scripts. Profile scripts start
automatically when PowerShell starts and their purpose is to customize your PowerShell
environment. You read more about profile scripts in Chapter 10.
Programs in special subdirectories: You can simply enter the program name to launch the
program if the program is located in one of the special folders specified in the Path
environment variable. Almost all relevant tools can be launched that way.
Table of Contents | About PowerShell Plus 33 Sponsors | Resources | BBS Technologies
Speci0!ing a path: You must tell the console where it is if the program is located somewhere
else. To do so, specify the absolute or relative path name of the program.
3atch out 0or white space characters: if white space characters occur in path names,
enclose the entire path in quotes so that PowerShell doesn't interpret white space characters
as separators. It doesn't matter whether you use double quotation marks ("") or single
quotation marks ( ' ' ); you just have to be consistent. Stick to single quotes. For example,
PowerShell "resolves" text in double quotation marks, replacing variables with their values.
The <A< changes string into commands: PowerShell doesn't treat text in quotes as a
command. Prefix string with "&" to actually execute it. The "&" symbol allows you to execute
any string just as if you had entered the text directly on the command line.
& $"note" + "pad"&
If you have to enter a very long path names, remember (Tab), the
key for automatic completion:
C:\(Tab)
Press (Tab) again and again until the suggested subdirectory is the one you
are seeking. Add a "\" and press (Tab) once again in order to specify the next
subdirectory.
The moment a white space character turns up in a path, AutoComplete also
puts the path in quotation marks and inserts an "&" before it. However, if you
want to add further subdirectories, you must first remove the last quotation
mark with (Backspace).
Cmdlets: <Benuine< PowerShell Commands
PowerShells internal commands are called 'cmdlets'. The "mother" of all cmdlets is called 'et-
"o!!and:
Get-Command -commandType cmdlet
It retrieves a list of all available cmdlets. Cmdlet names always consist of an action (verb) and
something that is acted on (noun). This naming convention helps you to find the right command.
Let's take a look at how the system works.
If you're looking for a command for a certain task, you should first select the action that best
describes the task. There are relatively few actions that the strict PowerShell naming conditions
permit (Table 2.2). If you know that you want to obtain something, the proper action is "get." That
already gives you the first part of the command name, and all you have to do now is to take a look
at a list of commands that are likely candidates:
Get-Command -verb get
CommandType Name Definition
Table of Contents | About PowerShell Plus 34 Sponsors | Resources | BBS Technologies
----------- ---- ----------
cmdlet Get-Acl Get-Acl [[-Path] <String[]>] [-A...
cmdlet Get-Alias Get-alias [[-Name] <String[]>] [...
cmdlet Get-Authenticode Get-AuthenticodeSignature [-File...
Signature
cmdlet Get-ChildItem Get-ChildItem [[-Path] <String[]...
cmdlet Get-Command Get-Command [[-ArgumentList] <Ob...
cmdlet Get-Content Get-Content [-Path] <String[]> [...
cmdlet Get-Credential Get-Credential [-Credential] <PS...
cmdlet Get-Culture Get-Culture [-Verbose] [-Debug] ...
cmdlet Get-Date Get-Date [[-Date] <DateTime>] [-...
cmdlet Get-EventLog Get-EventLog [-LogName] <String>...
cmdlet Get-Execution Get-ExecutionPolicy [-Verbose] [...
Policy
cmdlet Get-Help Get-Help [[-Name] <String>] [-Ca...
cmdlet Get-History Get-History [[-Id] <Int64[]>] [[...
cmdlet Get-Host Get-Host [-Verbose] [-Debug] [-E...
cmdlet Get-Item Get-Item [-Path] <String[]> [-Fi...
cmdlet Get-ItemProperty Get-ItemProperty [-Path] <String...
cmdlet Get-Location Get-Location [-PSProvider <Strin...
cmdlet Get-Member Get-Member [[-Name] <String[]>] ...
cmdlet Get-PfxCertificate Get-PfxCertificate [-FilePath] <...
cmdlet Get-Process Get-Process [[-Name] <String[]>]...
cmdlet Get-PSDrive Get-PSDrive [[-Name] <String[]>]...
cmdlet Get-PSProvider Get-PSProvider [[-PSProvider] <S...
cmdlet Get-PSSnapin Get-PSSnapin [[-Name] <String[]>...
cmdlet Get-Service Get-Service [[-Name] <String[]>]...
cmdlet Get-TraceSource Get-TraceSource [[-Name] <String...
cmdlet Get-UICulture Get-UICulture [-Verbose] [-Debug...
cmdlet Get-Unique Get-Unique [-InputObject <PSObje...
cmdlet Get-Variable Get-Variable [[-Name] <String[]>...
cmdlet Get-WmiObject Get-WmiObject [-Class] <String> ...
As you see, the relevant cmdlet 'et-"o!!and comes from the "get" group.
Action )escription
Add Add
"lear Delete
"o!pare Compare
"on$ert Convert
"opy Copy
Table of Contents | About PowerShell Plus 35 Sponsors | Resources | BBS Technologies
-(port Export
/or!at Format
'et Acquire
'roup Group
4!port Import
Measure Measure
Mo$e Move
5ew Create new
,ut Output
Read Read
Re!o$e Remove
Rena!e Rename
Resol$e Resolve
Restart Restart
Resu!e Resume
Select Select
Set Set
Sort Sort
Split Split
Table of Contents | About PowerShell Plus 36 Sponsors | Resources | BBS Technologies
Start Start
Stop Stop
Suspend Suspend
6ee Split up
6est Test
6race Trace
<pdate Update
Write Write
Table %$%: The most important standard actions and their descriptions
You can look up help for any cmdlet using 'et-0elp:
Get-Help Get-Command -detailed
You can easily discover commands for certain actions because 'et-"o!!and also allows wildcards:
Get-Command *help* -CommandType cmdlet
CommandType Name Definition
----------- ---- ----------
cmdlet Get-Help Get-Help [[-Name] <String>] [-Category...
8sing Parameters
Parameters add information so a cmdlet knows what to do. Once again, 'et-0elp will show you
which parameter are supported by any given cmdlet. For example, the cmdlet 'et-"hild4te! lists
the contents of the current subdirectory. The contents of the current folder will be listed if you enter
the cmdlet without additional parameters:
Get-ChildItem
For example, if you'd prefer to get a list of the contents of another subdirectory, you should enter
the subdirectory name after the cmdlet:
Table of Contents | About PowerShell Plus 37 Sponsors | Resources | BBS Technologies
Get-ChildItem c:\5indo56
You can use 'et-0elp to output full help on 'et-"hild4te! to find out which parameters are
supported:
Get-Help Get-ChildItem -full
This will give you comprehensive information as well as several examples. Of particular interest is
the "Parameters" section:
(path CstringDEF
Specifies a path to one or more locations. Wildcards are permitted. The default location is the
current directory (.).
Required? false
Position? 1
Default value
<NOTE: if not specified uses the Current
location>
Accept pipeline input? true (ByValue, ByPropertyName)
Accept wildcard characters? true
(include CstringDEF
Retrieves only the specified items. The value of this parameter qualifies the Path parameter. Enter a
path element or pattern, such as "*.txt". Wildcards are permitted.
The Include parameter is effective only when the command includes the Recurse parameter or the
path leads to the contents of a directory, such as C:\Windows\*, where the wildcard character
specifies the contents of the C:\Windows directory.
Required? false
Position? named
Default value
Accept pipeline input? false
Table of Contents | About PowerShell Plus 38 Sponsors | Resources | BBS Technologies
Accept wildcard characters? true
(exclude CstringDEF
Omits the specified items. The value of this parameter qualifies the Path parameter. Enter a path
element or pattern, such as "*.txt". Wildcards are permitted.
This parameter does not work properly in this cmdlet.
Required? false
Position? named
Default value
Accept pipeline input? false
Accept wildcard characters? true
(0ilter CstringF
Specifies a filter in the provider's format or language. The value of this parameter qualifies the Path
parameter. The syntax of the filter, including the use of wildcards, depends on the provider. Filters
are more efficient than other parameters, because the provider applies them when retrieving the
objects, rather than having Windows PowerShell filter the objects after they are retrieved.
Required? false
Position? 2
Default value
Accept pipeline input? false
Accept wildcard characters? true
Table of Contents | About PowerShell Plus 39 Sponsors | Resources | BBS Technologies
(name CSwitchParameterF
Retrieves only the names of the items in the locations. If you pipe the output of this command to
another command, only the item names are sent.
Required? false
Position? named
Default value False
Accept pipeline input? false
Accept wildcard characters? false
(recurse CSwitchParameterF
Gets the items in the specified locations and in all child items of the locations.
Recurse works only when the path points to a container that has child items, such as C:\Windows or
C:\Windows\*, and not when it points to items that do not have child items, such as
C:\Windows\*.exe.
Required? false
Position? named
Default value False
Accept pipeline input? false
Accept wildcard characters? false
(0orce CSwitchParameterF
Overrides restrictions that prevent the command from succeeding, just so the changes do not
compromise security. For example, Force will override the read-only attribute or create directories to
complete a file path, but it will not attempt to change file permissions.
Table of Contents | About PowerShell Plus 40 Sponsors | Resources | BBS Technologies
Required? false
Position? named
Default value False
Accept pipeline input? false
Accept wildcard characters? false
(codeSigningCert CSwitchParameterF
Retrieves only the certificates that have code signing authority. This parameter is valid only when
using the Windows PowerShell Certificate provider. For more information, type "get-help
about_provider" and "get-help about_signing".
Required? false
Position? named
Default value
Accept pipeline input? false
Accept wildcard characters? false
(literalPath CstringDEF
Specifies a path to one or more locations. Unlike Path, the value of LiteralPath is used exactly as it is
typed. No characters are interpreted as wildcards. If the path includes escape characters, enclose it
in single quotation marks. Single quotation marks tell Windows PowerShell not to interpret any
characters as escape sequences
Required? true
Position? 1
Default value
Table of Contents | About PowerShell Plus 41 Sponsors | Resources | BBS Technologies
Accept pipeline input? true (ByPropertyName)
Accept wildcard characters? false
CCommonParametersF
This cmdlet supports the common parameters: -Verbose, -Debug, -ErrorAction, -ErrorVariable, and -
OutVariable. For more information, type, "get-help about_commonparameters".
'et-"hild4te! supports a total of its own eight parameters as well as several "o!!onPara!eters.
Every parameter has a specific name that begins with a hyphen.
8sing 7amed Parameters
Named parameters really work like key-value pairs. You specify the name of a parameter (which
always starts with a hyphen), then a space, then the value you want to assign to the parameter. If
you ever used VBA (Visual Basic for Applications), it is similar to named parameters there. Let's say
you'd like to list all files with the extension +.e(e that are located somewhere in the folder
c)*windows or in one of its subdirectories, you could use this command:
Get-ChildItem -path c:\5indo56 -filter *.exe -recurse -name
There are clever tricks to make life easier. You don't have to specify the complete parameter name
as it is OK to type out just enough to make clear which parameters you mean:
Get-ChildItem -pa c:\5indo56 -fi *.exe -r -n
Don't worry: If you are getting too lazy and shorten parameter names too much, PowerShell will
report ambiguities and specify the parameter it can no longer assign clearly:
Get-ChildItem -pa c:\5indo56 -f *.exe -r -n
,et-'hild.tem : /arameter cannot be processed because
the parameter name 0f0 is ambiguous. /ossible matches
include: -1ilter -1orce.
At line:1 char:12
,et-'hild.tem !!!! -pa c:(%indo%s -f 3.exe -r -n
You can also turn off parameter recognition. This is necessary in
the rare event that an argument reads like a parameter name, and
so must be entered in such a way that it is not interpreted as a
parameter. So, if you need to output the text "-BackgroundColor"
Table of Contents | About PowerShell Plus 42 Sponsors | Resources | BBS Technologies
with Write-0ost, this will likely result in a conflict. PowerShell would assume
that you meant -9ackround"olor and notify you that the argument for this
parameter is missing.
Write-Host -BackgroundColor
Write-Host : Missing an argument for parameter
'BackgroundColor'. Specify a parameter of type
"System.consoleColor" and try again.
At line:1 char:27
+ Write-Host -BackgroundColor <<<<
You always have the choice of including the text in quotes. Or you can
expressly turn off parameter recognition by typing "--". Everything following
these two symbols will no longer be recognized as a parameter:
Write-Host "-BackgroundColor"
-BackgroundColor
Write-Host -- -BackgroundColor
-BackgroundColor
Switch Parameter
Sometimes, parameters really are no key-value pairs. Whenever a parameter represents a boolean
value (true or false), most often it is used as a switch parameter (present or not). So, switch
parameters stand for themselves, no values follow. If they're specified, they turn on a certain
function. If they're left out, they don't turn on the function. For example, the parameter -recurse
ensures that 'et-"hild4te! searches not only the -path specified subdirectories but all
subdirectories. And the switch parameter -na!e makes 'et-"hild4te! output only the names of files
(as string rather than rich file and folder objects).
The help on 'et-"hild4te! clearly identifies switch parameters. "<SwitchParameter>" follows the
parameter names:
-recurse <SwitchParameter>
Gets the items in the specified locations and all child
items of the locations.
(...)
Table of Contents | About PowerShell Plus 43 Sponsors | Resources | BBS Technologies
Positional Parameters
Some parameters have fixed positions while others are "named" - you can find out in Help when you
look at the cmdlet parameters and check out their "Position" property. Named parameters are easy:
they always need to be named so you always have to specify the parameter name, a space, and
then the parameter value. Positional parameters work the same but you can also specify them as
positional. So when a parameter has a position of 1, the first "unnamed" parameter is assigned to it.
That's why you could have expressed the command we just discussed in one of the following ways:
Get-ChildItem c:\5indo56 *.exe -recurse -name
Get-ChildItem -recurse -name c:\5indo56 *.exe
Get-ChildItem -name c:\5indo56 *.exe -recurse
In all three cases, PowerShell identifies and eliminates the named arguments -recurse and -na!e
first because they are clearly specified. The remaining are arguments are "unnamed" and need to be
assigned based on their position:
Get-ChildItem c:\5indo56 *.exe
The parameter -path has the position 1, and no value has yet been assigned to it. So, PowerShell
attaches the first remaining argument to this parameter.
-path <string[]>
Specifies a path to one or more locations. Wildcards are
permitted. The default location is the current directory (.).
Required? false
Position? 1
Standard value used <NOTE: if not specified uses
the Current location>
Accept pipeline input? true (ByValue, ByPropertyName)
Accept wildcard characters? true
The parameter -filter has the position 2. Consequently, it is assigned the second remaining
argument. The position specification makes it easier to use a cmdlet because you don't have to
specify any parameter names for the most frequently and commonly used parameters.
Here is a tip: In daily interactive PowerShell scripting, you want short and fast commands so use
aliases, positional parameters, and abbreviated parameter names. Once you write PowerShell
scripts, you should not use these shortcuts and instead use the true cmdlet names and stick to fully
named parameters. One reason is that scripts should be portable and not depend on specific aliases
you may have defined. Second, scripts are more complex and need to be as readable and
understandable as possible. Named parameters help other people better understand what you are
doing.
Common Parameters
Cmdlets also support a set of generic "CommonParameters":
<CommonParameters>
Table of Contents | About PowerShell Plus 44 Sponsors | Resources | BBS Technologies
This cmdlet supports the common parameters: -Verbose,
-Debug, -ErrorAction, -ErrorVariable, and -OutVariable.
For more information, type "get-help about_commonparameters".
These parameters are called "common" because they are permitted for (nearly) all cmdlets and
behave the same way.
Common
Parameter
T!pe )escription
-8erbose Switch
Generates as much information as possible. Without
this switch, the cmdlet restricts itself to displaying
only essential information
-.ebu Switch
Outputs additional warnings and error messages that
help programmers find the causes of errors. You can
find more information in Chapter 11.
--rrorAction Value
Determines how the cmdlet responds when an error
occurs. Permitted values:
5oti7y"ontinue) reports error and continues (default)
5oti7yStop) reports error and stops
Silent"ontinue) displays no error message, continues
SilentStop) displays no error message, stops
4n:uire) asks how to proceed
You can find more information in Chapter 11.
--rror8ariable Value
Name of a variable in which in the event of an error
information about the error is stored. You can find
more information in Chapter 11.
-,ut8ariable Value
Name of a variable in which the result of a cmdlet is
to be stored. This parameter is usually superfluous
because you can directly assign the value to a
variable. The difference is that it will no longer be
displayed in the console if you assign the result to a
variable.
$result = Get-ChildItem
It will be output to the console and stored in a
variable if you assign the result additionally to a
variable:
Get-ChildItem -OutVariable re68lt
Table of Contents | About PowerShell Plus 45 Sponsors | Resources | BBS Technologies
Table %$-: Common parameters in effect for (nearly) all cmdlets
Aliases: Biving Commands *ther 7ames
Cmdlet names with their verb-noun convention are very systematic, yet not very practical. In every
day admin life, you want short and familiar commands. This is why PowerShell has a built-in alias
system as it comes with a lot of predefined aliases. This is why in PowerShell, both Windows admins
and UNIX admins can list folder contents. There are predefined aliases called "dir" and "ls" which
both point to the PowerShell cmdlet Get-ChildItem.
Get-ChildItem c:\
(ir c:\
l6 c:\
So, aliases have two important tasks in PowerShell:
+istorical: New commands are designed to be accessed under old conventional names to
facilitate the transition to PowerShell
Com0ort: Frequently used commands are meant to be accessed over short and concise
commands
,esolving Aliases
Use these lines if you'd like to know what "genuine" command is hidden in an alias:
$alias:Dir
Get-ChildItem
$alias:ls
Get-ChildItem
=alias).ir lists the element .ir of the drive alias). That may seem somewhat surprising because
there is no drive called alias) in the classic console. In contrast, PowerShell works with many
different virtual drives, and alias) is only one of them. If you want to know more, the cmdlet 'et-
PS.ri$e lists them all. You can also list alias) like any other drive with .ir. The result would be a list
of aliases in their entirety:
(ir alia6:
CommandType Name Definition
----------- ---- ----------
alias ac Add-Content
alias asnp Add-PSSnapin
alias clc Clear-Content
(...)
Table of Contents | About PowerShell Plus 46 Sponsors | Resources | BBS Technologies
You can also get the list of aliases using the cmdlet 'et-Alias. You receive a list of individual alias
definitions by using its parameter -na!e:
Get-alias -name (ir
Get-ChildItem
It's a little more complex to list all aliases for a given cmdlet. Just use the PowerShell pipeline which
feeds the result of a command into the next one and chains together commands. The concept of the
pipeline will be discussed in detail in Chapter 5. You may not really grasp the significance of the next
command until after you've read this chapter. Nevertheless, here it is:
Get-Alias | Where-Object 9$_.Definition -eq "Get-ChildItem":
Here, the list of aliases that 'et-Alias generates is fed into the next cmdlet, Where-,b>ect. This
cmdlet is a pipeline filter and allows only those objects to pass through that meet the specified
condition. In this case, the condition is called "$_.Definition -eq 'Get-ChildItem' ". =3 represents the
current pipeline object. The condition checks the .e7inition property in this object, and if it equals
the "Get-ChildItem" string, the object can continue to pass through the pipeline. If not, it is filtered
out.
CommandType Name Definition
----------- ---- ----------
alias gci Get-ChildItem
alias ls Get-ChildItem
alias Dir Get-ChildItem
As it turns out, there's even a third alias for 'et-"hild4te! called "ci". Generally speaking,
PowerShell allows you to find several approaches to the same goal so you could have found the
same result by entering:
(ir alia6: | Out-String -Stream | Select-String "Get-ChildItem"
Here, the PowerShell pipeline works with conventional string, not objects. ,ut-Strin converts the
objects that .ir alias) generates into string. The parameter -Strea! makes sure each objects' string
representation is immediately forwarded to the next command in the pipeline. Select-Strin filters a
string, allowing only a string to pass through that includes the search word you specified.
Don't worry; all the techniques scratched here will be covered in detail in upcoming chapters. Here is
another example for you to try out the power of the PowerShell pipeline:
(ir alia6: | Group-Object de2inition
Here, the individual alias definitions are again fed through the PowerShell pipeline, yet this time the
cmdlet 'roup-,b>ect grouped the objects by their definition property. That's why 'roup-,b>ect
generates a neatly ordered list of all cmdlets, for which there are shorthand expressions for aliases.
In the Group column, you'll find the respective aliases in braces ({}).
Count Name Group
----- ---- -----
1 Add-Content {ac}
1 Add-PSSnapin {asnp}
1 Clear-Content {clc}
Table of Contents | About PowerShell Plus 47 Sponsors | Resources | BBS Technologies
1 Clear-Item {cli}
1 Clear-ItemProperty {clp}
1 Clear-Variable {clv}
3 Copy-Item {cpi, cp, copy}
1 Copy-ItemProperty {cpp}
1 Convert-Path {cvpa}
1 Compare-Object {diff}
1 Export-Alias {epal}
1 Export-Csv {epcsv}
1 Format-Custom {fc}
1 Format-List {fl}
2 ForEach-Object {foreach, %}
1 Format-Table {ft}
1 Format-Wide {fw}
1 Get-Alias {gal}
3 Get-Content {gc, cat, type}
3 Get-ChildItem {gci, ls, Dir}
1 Get-Command {gcm}
1 Get-PSDrive {gdr}
3 Get-History {ghy, h, history}
1 Get-Item {gi}
2 Get-Location {gl, pwd}
1 Get-Member {gm}
1 Get-ItemProperty {gp}
2 Get-Process {gps, ps}
1 Group-Object {group}
1 Get-Service {gsv}
1 Get-PSSnapin {gsnp}
1 Get-Unique {gu}
1 Get-Variable {gv}
1 Get-WmiObject {gwmi}
1 Invoke-Expression {iex}
2 Invoke-History {ihy, r}
1 Invoke-Item {ii}
1 Import-Alias {ipal}
1 Import-Csv {ipcsv}
3 Move-Item {mi, mv, move}
1 Move-ItemProperty {mp}
1 New-Alias {nal}
2 New-PSDrive {ndr, mount}
1 New-Item {ni}
1 New-Variable {nv}
1 Out-Host {oh}
1 Remove-PSDrive {rdr}
6 Remove-Item {ri, rm, rmdir, del...}
2 Rename-Item {rni, ren}
1 Rename-ItemProperty {rnp}
1 Remove-ItemProperty {rp}
1 Remove-PSSnapin {rsnp}
1 Remove-Variable {rv}
1 Resolve-Path {rvpa}
1 Set-Alias {sal}
1 Start-Service {sasv}
Table of Contents | About PowerShell Plus 48 Sponsors | Resources | BBS Technologies
1 Set-Content {sc}
1 Select-Object {select}
1 Set-Item {si}
3 Set-Location {sl, cd, chdir}
1 Start-Sleep {sleep}
1 Sort-Object {sort}
1 Set-ItemProperty {sp}
2 Stop-Process {spps, kill}
1 Stop-Service {spsv}
2 Set-Variable {sv, set}
1 Tee-Object {tee}
2 Where-Object {where, ?}
2 Write-Output {write, echo}
2 Clear-Host {clear, cls}
1 Out-Printer {lp}
1 Pop-Location {popd}
1 Push-Location {pushd}
Creating Gour *wn Aliases
Anyone can create a new alias, which is a shortcut for another command. The cmdlet Set-Alias adds
additional alias definitions. You could actually override commands with aliases since aliases have
precedence over other commands. Take a look at the next example:
0dit
Set-Alias edit notepad.exe
0dit
-dit typically launches the console-based Editor program. To exit without completely closing the
console window, press (Alt)+(F) and then (X).
If you create a new alias called "Edit" and set it to "notepad.exe", the command -dit will be re-
programmed. The next time you enter it, PowerShell will no longer run the old Editor program, but
the Notepad.
$alias:edit
,emovingHor Permanentl! ,etainingHan Alias
How do you remove aliases? You don't. All new aliases are discarded as soon as you exit PowerShell.
All of your own aliases will be gone the next time you start PowerShell. "Built-in" aliases like "dir"
and "cd" will still be there.
If you'd like to keep your own aliases permanently, you have the following options:
'anuall! each time: Set your aliases after every start manually using Set-Alias. That is, of
course, rather theoretical.
Automated in a pro0ile: Let your alias be set automatically when PowerShell starts: add
your aliases to a start profile. You'll learn how to do this in Chapter 10.
Table of Contents | About PowerShell Plus 49 Sponsors | Resources | BBS Technologies
&mport and export: You can use the built-in import and export function for aliases.
For example, if you'd like to export all currently defined aliases as a list to a file, enter:
Export-Alias
Because you haven't entered any file names after -(port-Alias, the command will ask you what the
name are under which you want to save the list. Type in:
alias1 (Enter)
The list will be saved. You can look at the list afterwards and manipulate it. For example, you might
want the list to include a few of your own alias definitions:
Notepad alia6!
You can import the list to activate the alias definitions:
Import-Alias alia6!
.mport-Alias : Alias not allo%ed because an alias %ith the
name "ac" already exists.
At line:1 char:14
.mport-Alias !!!! alias1
4!port-Alias notifies you that it couldn't create some aliases of the list because these aliases already
exist. Specify additionally the option -/orce to ensure that 4!port-Alias overwrites existing aliases, :
Import-Alias alia6! -Force
You could add the 4!port-Alias instruction to your start profile and
specify a permanent path to the alias list. This would make
PowerShell automatically read this alias list when it starts. Later,
you can add new aliases. Then, it would suffice to update the alias
list with -(port-Alias and to write over the old file. This is one way
for you to keep your aliases permanently.
*verwriting Alias )e0initions and )eleting Them 'anuall!
You can overwrite aliases with new definitions any time. Just redefine the alias with the cmdlet Set-
Alias. Use this command if you'd like to remove an alias completely and don't want to wait until you
end PowerShell:
(el alia6:edit
Table of Contents | About PowerShell Plus 50 Sponsors | Resources | BBS Technologies
This instruction deletes the "Edit" alias. Here, the uniform provider approach becomes evident. The
very same "Del" command would allow you to delete files and subdirectories in the file system as
well. Perhaps you're already familiar with the command from the classic console:
(el C:\garbage.txt
Here is an example that finds all aliases that point to no valid
target, which is a great way of finding outdated or damaged
aliases:
Get-Alias | ForEach-Object 9
if $!$Get-Command $_.Definition -ea ;ilentl<Contin8e&& 9$_::
"unctions: <Expanded< Aliases
Aliases are simple shortcuts to call commands with another name (shortcut names), or to make the
transition to PowerShell easier (familiar names). The arguments of a command can never be
included in an alias, though. If you want that, you will need to use functions.
Calling Commands with Arguments
If you find yourself using the command pin quite often to verify network addresses, you may want
to make this easier by creating a shortcut that not only calls ping.exe, but also appends standard
arguments to it. Let's see how you could automate this call:
ing -n ! -w !"" !".!".!".!"
Aliases won't work in this case because they can't specify command arguments. Functions can since
they are more flexible:
function =8ic>ping 9 ping -n ! -w !"" $args :
=8ic>ping !".!".!".!"
Pinging 10.10.10.10 with 32 bytes of data:
Reply from 10.10.10.10: bytes=32 time<1ms TTL=128
Ping statistics for 10.10.10.10:
Packets: Sent = 1, Received = 1, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 0ms, Average = 0ms
Set-Alias =p =8ic>ping
=p !".!".!".!"
Pinging 10.10.10.10 with 32 bytes of data:
Reply from 10.10.10.10: bytes=32 time<1ms TTL=128
Table of Contents | About PowerShell Plus 51 Sponsors | Resources | BBS Technologies
Ping statistics for 10.10.10.10:
Packets: Sent = 1, Received = 1, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 0ms, Average = 0ms
Unlike alias definitions, you can specify complex code inside of braces in functions. So, you are no
longer limited to just specify a single command, but can also add any argument you want to be part
of the call. =ars in this connection acts as placeholder for the arguments that you assign to the
function.
Creating Shortcut Commands
You may have noticed that PowerShell doesn't accept console commands like the following one,
which do work in the classic console:
Cd..
The term "'d.." is not recognized as a cmdlet, function, operable
program, or script file. Verify the term and try again.
At line:1 char:12
5et-6ocation.. !!!!
The reason is that PowerShell is more strict and needs a space as delimiter between command and
argument. "d is an alias and points to the cmdlet Set-Location. If you omit a space, PowerShell tried
to find a command called "Cd.." and since it can't find one, it outputs an exception. The solution is
rather easy: you should define your own "cd.." command by defining a function with that name:
function Cd.. 9 Cd .. :
Cd..
The entry Cd.. works immediately because now PowerShell is running your new function. You can
add many other shortcuts this way.
Functions have exactly the same lifespan as aliases. As soon as
you exit PowerShell, it "forgets" all new aliases and functions that
you added. If you want to retain your PowerShell functions, you
should put them into one of the profile scripts that PowerShell runs
automatically when it starts. This is covered in Chapter 10.
&nvoking "iles and Scripts
To run files (like documents or scripts), PowerShell uses the same rules that apply to executables:
either, you specify an absolute or relative path, or the file needs to be located in one of the special
trustworthy folders defined in the Path environment variable.
Table of Contents | About PowerShell Plus 52 Sponsors | Resources | BBS Technologies
# Save information on all running processes to HTML file
# (lasts several seconds):
Get-Process | ConvertTo-Html | Out-File test.htm
# File cannot be opened directly:
test.htm
The term "test.htm" is not recognized as a cmdlet, function,
operable program, or script file. Verify the term and try again.
At line:1 char:8
test.htm !!!!
# Specify a relative or absolute path name:
.\test.htm
Starting Scripts
Scripts and batch files are pseudo-executables as they contain script code that can be executed by a
command line interpreter.
,unning 4atch "iles
Batch files are text files with the extension ".bat". They may include all the commands allowed in a
classic cmd.exe console. When a batch file is opened, the classic console immediately starts to
execute the commands it contains. Let's check it out. First, create this test file:
Notepad ping.bat
Now enter this text:
?echo o22
echo .n attac>er can do dangero86 thing6 here
pa86e
(ir %5indir%
pa86e
(ir %5indir%\6<6tem
Save the text and close Notepad. Your batch file is ready for action. Try to launch the batch file by
entering its name:
ing
The batch file won't run. Because it has the same name and you didn't specify any IP address or
Web site address, the Ping command spits out its internal help message. If you want to launch your
batch file, you're going to have to specify either the relative or absolute path name.
.\ping
Your batch file opens, then immediately run the commands it contains.
Table of Contents | About PowerShell Plus 53 Sponsors | Resources | BBS Technologies
PowerShell has just defended a clever attack. If you were using the classic console, you would have
been fooled by the attacker. All you have to do is switch over to the classic console and see for
yourself:
Cmd
ing !".!".!".!"
An attacker can do dangerous things here
Press any key . . .
If an attacker had smuggled a batch file named "ping.bat" into your current folder, then the Ping
command, harmless though it might seem, could have had catastrophic consequences. A classic
console doesn't distinguish between files and commands. It looks 7irst in the current folder, finds the
batch file, and executes it immediately. Such a mix-up would never happen in the PowerShell
console. So, return to your much safer PowerShell environment:
0-it
,unning I4Script "iles
VBScript is another popular automation language as its scripts are tagged with the file extension
".vbs". What we have just discussed about batch files also applies to these scripts:
Notepad test.vbs
Enter this VBScript code in Notepad:
Set wmi = GetObject("winmgmts:")
Set collection = wmi.ExecQuery("select * from Win32_Process")
For Each process in collection
WScript.Echo process.getObjectText_
Next
You should know how to run this script:
.\test.vbs (Enter)
You should first switch the Windows Script Host into the console
display before running the script so it will output its results directly
within the console. If you don't do this, every output will be shown
in its own window and you'll have to manually close each one.
76cript //H:C;cript
And this is the way to switch it back to Windows display:
7;cript //H:7;cript
Table of Contents | About PowerShell Plus 54 Sponsors | Resources | BBS Technologies
You can also run your VBS script without switching as a console script by
directly specifying the script host you want:
C;cript test.vbs
The script lists all running processes and provides many interesting details on
every process. VBScript is a very versatile and powerful automation
language, and it's important to show how you can use VBScript files in
PowerShell.
,unning PowerShell Scripts
PowerShell has its own script files with the file extension ".ps1". While you will learn much more
about PowerShell scripts in Chapter 10, you already know enough to write your first script:
Notepad test.ps1
Enter in Notepad any PowerShell command you like. Everything you've successfully entered in the
console up to now is allowed. PowerShell scripts function very much like the batch files of the classic
console: if the script is opened later, PowerShell works through everything in your script one step at
a time, just as if you had directly entered each line one-by-one in the console.
(ir
Get-PSProvider
help (ir
Try to bring it to life after you've saved your script:
.\test.ps1
1ile "':($sers($serA(test.ps1" cannot be loaded because the
execution of scripts is disabled on this system. /lease see
"get-help about7signing" for more details.
At line:1 char:18
.(test.ps1 !!!!
You'll probably receive an error message similar to the one in the above example. In PowerShell, all
scripts are at first disabled and cannot be started. PowerShell will start scripts only once you enabled
them (for which you need admin privileges since a regular user cannot change this setting). You can
give your permission by entering Set--(ecutionPolicy:
Set-ExecutionPolicy @emote;igned
Table of Contents | About PowerShell Plus 55 Sponsors | Resources | BBS Technologies
This grants permission to run locally stored PowerShell scripts as scripts from the Internet remain
prohibited unless they have a valid signature. The implications of signatures and other security
settings will be discussed in Chapter 10. For now, the command described above should be enough
for you to experiment with your own PowerShell scripts. To restore the original setting and prohibit
PowerShell scripts, you should enter:
Set-ExecutionPolicy (e2a8lt
Summar!
The PowerShell console runs all kinds of commands interactively: you enter a command, and the
console will more or less immediately return the results.
Cmdlets are PowerShell's own internal commands. A cmdlet name always consists of a description of
an action (verb), and the object of the action (noun). The cmdlet 'et-"o!!and will provide a list of
all cmdlets. 'et-0elp will also offer information about a particular cmdlet and can also search for
cmdlet names when you specify a search phrase and wildcards: Get-Command *Service*
In addition, you can use aliases, functions, and scripts in PowerShell. An alias is a shortcut name for
any other command, enabling you to create your own convenient shorthand expressions for
commands you use frequently. Functions and scripts combine several PowerShell commands. If you
enter a command and execute it by pressing (Enter), PowerShell looks for the command in this
order:
Alias: It will first look to see if your command corresponds to an alias. If it does, the
command will be executed that the alias designates. You can "overwrite" any other command
with an alias by using the cmdlet Set-Alias because aliases have highest priority.
"unction: If no alias could be found, PowerShell looks next for a function, which resembles
an alias, but can consist of many PowerShell instructions. You can wrap commands, including
frequently used arguments, in functions.
Cmdlet: If it's not possible to find a function, PowerShell looks for cmdlets, which are internal
PowerShell commands that conform to strict naming rules and whose names always consist of
a verb and a noun.
Application: PowerShell looks first for a cmdlet, and if it can't find any, it then searches for
external commands in the subdirectories specified in the Path environment variables. If you'd
like to use a command at some other location, then you must specify a relative or absolute
path name.
Script: If PowerShell can't find any external commands, it looks next for a script with the file
extension ".ps1". However, scripts are executed only when restrictions of the -(ecutionPolicy
are eased, allowing PowerShell scripts to be run.
"iles: If no PowerShell scripts are found, PowerShell keeps looking for other files. PowerShell
reports an error if your command doesn't match any files.
Again, use Get-Command to find out if there are any ambiguities.
The next line will list all commands that PowerShell knows that use
"ping" as a name.
Get-Command ping
Table of Contents | About PowerShell Plus 56 Sponsors | Resources | BBS Technologies
Type this if you'd like to find out whether there are commands with the same
names in differently named categories that conflict:
Get-Command -type cmdlet,function,alia6 | Group-Object name |
Where-Object 9$_.count -gt !:
Table of Contents | About PowerShell Plus 57 Sponsors | Resources | BBS Technologies
CHAPTER 3.
Variables
Variables
It is time to combine commands whenever a single PowerShell command can't solve your problem.
One way of doing this is by using variables. PowerShell can store results of one command in a
variable and then pass the variable to another command.
In addition, variables are rich 'objects' and can do much more than simply store data. In this
chapter, we'll explain what variables are and how you can use them to solve complex problems.
Topics Covered:
Your Own Variables
Selecting Variable Names
Assigning and Returning Values
Populating Several Variables with Values Simultaneously
Exchanging the Contents of Variables
Assigning Different Values to Several Variables
Overview of Variables in Use
Finding Variables
Verify Whether a Variable Exists
Deleting Variables
Using Special Variable Cmdlets
Table 3.1: Cmdlets for managing variables
Write-Protecting Variables: Creating Constants
Variables with Description
"Automatic" PowerShell Variables
Environment Variables
Reading Particular Environment Variables
Searching for Environment Variables
Creating New Environment Variables
Deleting and Modifying Environment Variables
Permanent Modifications of Environment Variables
Drive Variables
Directly Accessing File Paths
Table 3.2: Variable areas made available by external providers
Ad-hoc Variables: Sub-Expressions
Scope of Variables
Automatic Restriction
Changing Variable Visibility
Advantage of Lifting Visibility Restrictions: Clear and Unambiguous Start Conditions
Setting the Scope of Individual Variables
Table 3.3: Variable scopes and validity of variables
Table 3.4: Practical usage of scope allocations
Variable Types and "Strongly Typing"
Assigning Fixed Types
The Advantages of Specialized Types
Table 3.5: Variable types
Variable Management: Behind the Scenes
Table of Contents | About PowerShell Plus 58 Sponsors | Resources | BBS Technologies
Subsequent Modification of Variables Options
Activating Write-Protection
Table 3.6: Options of a PowerShell variable
Type Specification of Variables
Verifying and Validating Variable Contents
Table 3.7: Available variable validation classes
Summary
Gour *wn Iariables
Variables store information temporarily so you can take the information contained in a variable and
process it in further steps.
# Create variables and assign to values
$amount = !2"
$VAT = ".!A
# Calculate:
$result = $amount * $VAT
# Output result
$result
22.8
# Replace variables in text with values:
$text = "Net amount $amount matches gross amount $result"
$text
Net amount 120 matches gross amount 142.8
PowerShell creates new variables automatically so there is no need to specifically "declare"
variables. Simply assign data to a variable. The only thing you need to know is that variable names
are always prefixed with a "$".
You can then output the variable content by entering the variable name, or you can merge the
variable content into text strings. To do that, just make sure the string is delimited by double-
quotes. Single-quoted text will not resolve variables.
Selecting Iariable 7ames
In PowerShell, a variable name always begins with a dollar sign "$". The rest of the name may
consist of almost anything you want: letters, numbers, and the underline character. PowerShell
variable names are not case sensitive.
There is only one exception: certain special characters have special meaning for PowerShell. While
you can still use those special characters in variable names, you will then need to enclose the
variable name in braces. The best suggestion is not to use PowerShell's special characters in variable
names to avoid braces:
Table of Contents | About PowerShell Plus 59 Sponsors | Resources | BBS Technologies
# Variable names with special characters belong in braces:
$9thi6 4aria,le name i6 "unusual," ,8t permitted: = "Hello World"
$9thi6 4aria,le name i6 "unusual," ,8t permitted:
Hello World
Assigning and ,eturning Ialues
The assignment operator "=" sets a variable to a specified value. You can assign almost anything to
a variable, even complete command results:
# Temporarily store results of a cmdlet:
$listing = Get-ChildItem c:\
$listing

Directory: Microsoft.PowerShell.Core\FileSystem::C:\
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 06.26.2007 15:36 2420
d---- 05.04.2007 21:06 ATI
d---- 08.28.2006 18:22 Documents and settings
d---- 08.08.2007 21:46 EFSTMPWP
d---- 04.28.2007 02:18 perflogs
(...)
# Temporarily store the result of a legacy external command:
$result = ipcon2ig
$result
Windows IP Configuration
Ethernet adapter LAN Connection:
Media state
. . . . . . . . . . . : Medium disconnected
Connection-specific DNS Suffix:
Ethernet adapter LAN Connection 2:
Media state
. . . . . . . . . . . : Medium disconnected
Connection-specific DNS Suffix:
Wireless LAN adapter wireless network connection:
Media state
. . . . . . . . . . . : Medium disconnected
Connection-specific DNS Suffix:
Populating Several Iariables with Ialues Simultaneousl!
Not only can the assignment operator assign values to a single variable, it can set the contents of
several variables in one step. For example, you could set a whole series of variables to one shared
initial value:
Table of Contents | About PowerShell Plus 60 Sponsors | Resources | BBS Technologies
# Populate several variables with the same value in one step:
$a = $b = $c = !
$a
1
$b
1
$c
1
Exchanging the Contents o0 Iariables
Now and then you might want to exchange the contents of two variables. In traditional programming
languages, that would require several steps:
$Value1 = !"
$Value2 = 2"
$Temp = $Value1
$Value1 = $Value2
$Value2 = $Temp
With PowerShell, swapping variable content is much easier. First of all, you can write several
statements in one line if you separate the statements from each other by semi-colon. Second,
assignment operators will accept several variables on each side that replace each other:
# Exchange variable values:
$Value1 = !"B $Value2 = 2"
$Value1, $Value2 = $Value2, $Value1
Assigning )i00erent Ialues to Several Iariables
The real trick in the last example is the comma. PowerShell always uses the comma to create a
variable array (a variable that holds more than one value). We'll be exploring these arrays in depth
in Chapter 4, but it is important for you to know now that the assignment operator also processes
arrays. If you state to its left and right an array having the same number of elements, then it will
assign the elements of the array on the right side to the elements of the array on the left side. This
is a way for you to use a single assignment operator to populate different variables with different
values. It can thus simplify the previous example even more:
# Populate several variables with the same value in one step:
$Value1, $Value2 = !",2"
$Value1, $Value2 = $Value2, $Value1
Table of Contents | About PowerShell Plus 61 Sponsors | Resources | BBS Technologies
*verview o0 Iariables in 8se
PowerShell keeps a record of all variable assignments, which is accessible via a virtual drive called
$ariable). To see all currently defined variables, you should just output this drive to a list:
(ir 4aria,le:
Don't be surprised to see not only variables you've created yourself, but also many more. The
reason: PowerShell also defines variables and calls them "automatic variables." You'll learn more
about this soon.
"inding Iariables
Using the $ariable) virtual drive makes it easy to find variables by allowing wildcards, just like in the
file system. If you'd like to see all the variables whose name begins with the letters "value", try this:
(ir 4aria,le:4al8e*
Name Value
---- -----
value2 20
value1 10
.ir lists the two variables =$alue1 and =$alue? as well as returning their current contents. You can
also use the .ir parameters -include and -e(clude (the alias for 'et-"hild4te!). The next example
uses the -e(clude para!eter to find all the variables that begin with the letters "value" but don't use
an "l" in their names:
(ir 4aria,le: -include 4al8e* -exclude *!*
Name Value
---- -----
value2 20
If you'd like to know which variables currently contain the value ?0, the solution isn't quite so
simple, yet it is still doable. It consists of several commands piped together.
dir 4aria,le: | Out-String -stream | Select-String " 20 "
value2 20
$ 20
Here, the output from .ir is passed on to ,ut-Strin@which converts the results of .ir into text. The
parameter -strea! ensures that every variable supplied by .ir is separately output as text. Select-
Strin selects the lines that include the desired value, filtering out the rest. To ensure that only the
desired value is found and not other values that contain the number 20 (like 200), white space is
added before and after the number 20.
Table of Contents | About PowerShell Plus 62 Sponsors | Resources | BBS Technologies
Ieri0! 3hether a Iariable Exists
To find out whether a variable already exists, you should again do as you would with the file system.
Using the cmdlet 6est-Path, you can verify whether a certain file exists. Similar to files, variables are
stored in their own "drive" called $ariable)and every variable has a path name that you can verify
with 6est-Path:
# Verify whether the variable $value2 exists:
Test-Path 4aria,le:\4al8e2
True
# verify whether the variable $server exists:
Test-Path 4aria,le:\6er4er
False
Whether a variable already exists or not doesn't usually matter. When you assign a value to an
existing variable, the new value will simply overwrite the old one. However, sometimes you might
want to assign a value only when the variable doesn't exist yet. Also, variables can be write-
protected so that you cannot easily overwrite an existing variable.
)eleting Iariables
Because variables are deleted automatically as soon as you exit PowerShell, you don't necessarily
need to clean them up manually. If you'd like to delete a variable immediately, again, do exactly as
you would in the file system:
# create a test variable:
$test = !
# verify that the variable exists:
(ir 4aria,le:\te*
# delete variable:
del 4aria,le:\te6t
# variable is removed from the listing:
(ir 4aria,le:\te*
8sing Special Iariable Cmdlets
To manage your variables, PowerShell provides you with the five separate cmdlets listed in Table
3.1. You won't need these for everyday tasks because, as you've just seen, the virtual drive
$ariable) enables you to perform the most important management tasks just as you do in the file
system. Only two of the five cmdlets really offer you new options:
1. 5ew-8ariable enables you to specify options, such as a description or write protection. This
makes a variable into a constant. Set-Variable does the same for existing variables.
2. 'et-8ariable enables you to retrieve the internal PowerShell variables store.
Table of Contents | About PowerShell Plus 63 Sponsors | Resources | BBS Technologies
Cmdlet )escription Example
"lear-
8ariable
Clears the contents of a variable, but not the variable
itself. The subsequent value of the variable is NULL
(empty). If a data or object type is specified for the
variable, by using "lear-8ariable the type of the
objected stored in the variable will be preserved.
Clear-
Variable a
sa!e as)
=a A =null
'et-
8ariable
Gets the variable object, not the value in which the
variable is stored.
Get-Variable
a
5ew-
8ariable
Creates a new variable and can set special variable
options.
New-
Variable
value 12
Re!o$e-
8ariable
Deletes the variable, and its contents, as long as the
variable is not a constant or is created by the
system.
Remove-
Variable a
sa!e as)
del
$ariable)*a
Set-
8ariable
Resets the value of variable or variable options such
as a description and creates a variable if it does not
exist.
Set-Variable
a 12
sa!e as)
=a A 1?
Table -$#: Cmdlets for managing variables
3rite(Protecting Iariables: Creating Constants
Constants store a constant value that cannot be modified. They work like variables with a write-
protection.
PowerShell doesn't distinguish between variables and constants. However, it does offer you the
option of write-protecting a variable. In the following example, the write-protected variable =test is
created with a fixed value of 100. In addition, a description is attached to the variable.
# Create new variable with description and write-protection:
New-Variable te6t -value !"" -description `
"test variable with write-protection" -option @eadCnl<
$test
100
Table of Contents | About PowerShell Plus 64 Sponsors | Resources | BBS Technologies
# Variable contents cannot be modified:
$test = 2""
The #ariable "test" cannot be o#er%ritten since it is a
constant or read-only.
At line:1 char:9
:test !!!! ; *88
The variable is now write-protected and its value may no longer be changed. You'll receive an error
message if you try to change it. You must delete the variable and re-define it if you want to modify
its value. Because the variable is write-protected, it behaves like a read-only file. You'll have to
specify the parameter -7orce to delete it:
del 4aria,le:\te6t -force
$test = 2""
A write-protected variable can still be modified by deleting it and creating a new copy of it. If you
need strong protection like in traditional constants, you should create a variable with the "onstant
option. This will change the variable to a proper constant that may neither be modified nor deleted.
Only when you quit PowerShell are constants removed. Variables with the "onstant option may only
be created with 5ew-8ariable. You'll get an error message if a variable already exists that has the
specified name:
#New-Variable cannot write over existing variables:
New-Variable te6t -value !"" -description `
"test variable with copy protection" -option Con6tant
+e%-Variable : A #ariable named "test" already exists.
At line:1 'har:14
+e%-Variable !!!! test -#alue 188 -description
"test #ariable %ith copy protection" -option 'onstant
# If existing variable is deleted, New-Variable can create
# a new one with the "Constant" option:
del 4aria,le:\te6t -force
New-Variable te6t -value !"" -description `
"test variable with copy protection" `
-option Con6tant
# variables with the "Constant" option may neither be
# modified nor deleted:
del 4aria,le:\te6t -force
<emo#e-.tem : #ariable "test" may not be remo#ed since it is a
constant or %rite-protected. .f the #ariable is %rite-protected,
carry out the process %ith the 1orce parameter.
At line:1 'har:2
del !!!! #ariable:(test -force
You can overwrite an existing variable by using the -7orce parameter of 5ew-8ariable. Of course, this
is only possible if the existing variable wasn't created with the "onstant option. Variables of the
constant type are unchangeable once they have been created, and -force does not change this:
Table of Contents | About PowerShell Plus 65 Sponsors | Resources | BBS Technologies
# Parameter -force overwrites existing variables if these do not
# use the "Constant" option:
New-Variable te6t -value !"" -description "test variable" -force
+e%-Variable : #ariable "test" may not be remo#ed since it is a
constant or %rite-protected.
At line:1 char:14
+e%-Variable !!!! test -#alue 188 -description "test #ariable"
# normal variables may be overwritten with -force without difficulty.
$available = !2'
New-Variable a4aila,le -value !"" -description "test variable" -force
Iariables with )escription
Variables can have an optional description that helps you keep track of what the variable was
intended for. However, this description appears to be invisible:
# Create variable with description:
New-Variable m<4aria,le -value !"" -description "test variable" -force
# Variable returns only the value:
$myvariable
100
# Dir and Get-Variable also do not deliver the description:
(ir 4aria,le:\m<4aria,le
Name Value
---- -----
myvariable 100
Get-Variable m<4aria,le
Name Value
---- -----
myvariable 100
By default, PowerShell only shows the most important properties of an object, and the description of
a variable isn't one of them. If you'd like to see the description, you have to explicitly request it. You
can do this by using the cmdlet /or!at-6able (you'll learn much about this in Chapter 5). Using
/or!at-6able@ you can specify the properties of the object that you want to see:
# variable contains a description:
dir 4aria,le:\m<4aria,le |
Format-Table Name, Dal8e, (e6cription -autosize
Name Value Description
---- ----- -----------
test 100 test variable
Table of Contents | About PowerShell Plus 66 Sponsors | Resources | BBS Technologies
<Automatic< PowerShell Iariables
PowerShell uses variables, too, for internal purposes and calls those "automatic variables." These
variables are available right after you start PowerShell since PowerShell has defined them during
launch. The drive $ariable) provides you with an overview of all variables:
(ir 4aria,le:
Name Value
---- -----
Error {}
DebugPreference SilentlyContinue
PROFILE C:\Users\Tobias Weltner\Documents\
WindowsPowerShell\Micro...
HOME C:\Users\Tobias Weltner
(...)
To understand the meaning of automatic variables, you can simply view their description:
(ir 4aria,le: | Sort-Object Name |
Format-Table Name, (e6cription -autosize -wrap
Name Description
---- -----------
$
? Execution status of last command.
^
_
ConfirmPreference Dictates when confirmation should be requested.
Confirmation is requested when the ConfirmImpact
of the operation is equal to or greater than
$ConfirmPreference. If $ConfirmPreference is
None, actions will only be confirmed when
Confirm is specified.
ConsoleFileName Name of the current console file.
DebugPreference Dictates action taken when an Debug message is
delivered.
Error
ErrorAction Dictates action taken when an Error message is
Preference delivered.
ErrorView Dictates the view mode to use when displaying
errors.
ExecutionContext The execution objects available to cmdlets.
false Boolean False
FormatEnumeration Dictates the limit of enumeration on formatting
Limit IEnumerable objects.
HOME Folder containing the current user's profile.
Host This is a reference to the host of this
Runspace.
MaximumAliasCount The maximum number of aliases allowed in a
session.
Table of Contents | About PowerShell Plus 67 Sponsors | Resources | BBS Technologies
MaximumDriveCount The maximum number of drives allowed in a
session.
MaximumErrorCount The maximum number of errors to retain in a
session.
MaximumFunctionCount The maximum number of functions allowed in a
session.
MaximumHistoryCount The maximum number of history objects to retain
in a session.
MaximumVariableCount The maximum number of variables allowed in a
session.
MyInvocation
NestedPromptLevel Dictates what type of prompt should be
displayed for the current nesting level.
null References to the null variable always return
the null value. Assignments have no effect.
OutputEncoding The text encoding used when piping text to a
native executable.
PID Current process ID.
PROFILE
ProgressPreference Dictates action taken when Progress Records
are delivered.
PSHOME Parent folder of the host application of this
Runspace.
PWD
ReportErrorShow Causes errors to be displayed with a description
ExceptionClass of the error class.
ReportErrorShow Causes errors to be displayed with the inner
InnerException exceptions.
ReportErrorShow Causes errors to be displayed with the source of
Source the error.
ReportErrorShow Causes errors to be displayed with a stack
trace.
StackTrace
ShellId The ShellID identifies the current shell. This
is used by #Requires.
StackTrace
true Boolean True
VerbosePreference Dictates the action taken when a Verbose message
is delivered.
WarningPreference Dictates the action taken when a Warning message
is delivered.
WhatIfPreference If true, WhatIf is considered to be enabled for
all commands.
Most automatic variables are very well documented. Variables are assigned to three categories:
8ser in0ormation: PowerShell permanently stores some important information. For
example, the path name of the standard profile in =0,M-. In addition, some standard
variables, like =true and =7alse, are set.
"ine adJustments: Numerous default settings allow the behavior of PowerShell to be
modified and customized. For example, you can set how detailed error messages are
reported, or whether a command should continue to execute, in the event of an error. You'll
learn more about this in Chapter 11.
Table of Contents | About PowerShell Plus 68 Sponsors | Resources | BBS Technologies
,unning time in0ormation: PowerShell returns valuable information when it executes
statements. For example, a function can determine who calls it, or a script can determine the
location of its folder.
In other respects, automatic variables are no different from the variables you define yourself as you
can read the contents and use them in much the same way:
# Verify user profile:
$HOME
C:\Users\UserA
# Verify PowerShell Process -id and access profile:
"current process -ID of PowerShell is $PID"
current process -ID of PowerShell is 6116
Get-Process -id $PID
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
656 22 107620 72344 334 118,69 6116 PowerShell
# Open the standard user profile in notepad for editing:
notepad $profile
To find out more, use Get-0elp:
Get-Help a,o8tE.8tomaticE4aria,le6
PowerShell write protects several of its automatic variables. While you
can read them, you can't modify them. That makes sense because
information, like the process-ID of the PowerShell console or the root
directory, should not be modified.
$pid = !2
'annot o#er%rite #ariable "/.=" because it is read-only or
constant.
At line:1 char:>
:pid !!!! ; 1*
A little later in this chapter, you'll find out more about how write-protection
works. You'll then be able to turn write-protection off and on for variables that
already exist. However, you should never do this for automatic variables because
that can cause the PowerShell console to crash. One reason is because
PowerShell continually modifies some variables. If you set them to read-only,
PowerShell may stop and not respond to any inputs.
Table of Contents | About PowerShell Plus 69 Sponsors | Resources | BBS Technologies
Environment Iariables
Older consoles do not typically have a variable system of their own that was as sophisticated as
PowerShell's. Instead, those consoles relied on "environment variables," which are managed by
Windows itself. Environment variables are important sources of information for PowerShell because
they include many details about the operating system. Moreover, while PowerShell's variable are
visible only inside of the current PowerShell session, environment variables can persist and thus can
be readable by other programs.
Working with environment variables in PowerShell is just as easy as working with internal
PowerShell variables. All you have to do is to tell PowerShell precisely which variable you mean. To
do this, you should specify the variable source at the beginning of the variable name. For
environment variables, it's en$).
,eading Particular Environment Iariables
You can read the location of the Windows folder of the current computer from a Windows
environment variable:
$env:windir
C:\Windows
By adding en$), you've instructed PowerShell not to look for the variable windir in the normal
PowerShell variable store, but in Windows environment variables. In other respects, the variable
behaves just like any other PowerShell variable. For example, you could embed it in the text:
"The Windows folder is here: $env:windir"
3he 7indo56 2older i6 here: C:\7indo56
You can just as easily use the variable with commands and switch over temporarily to the Windows
folder in the following way:
# save in current folder:
Push-Location
# change to Windows folder
cd $env:windir
(ir
# change back to initial location after executed task
Pop-Location
Searching 0or Environment Iariables
PowerShell keeps track of Windows environment variables and lists them in the en$) virtual drive.
So, if you'd like an overview of all existing environment variables, you should just list the contents of
the en$) drive:
(ir en4:
Table of Contents | About PowerShell Plus 70 Sponsors | Resources | BBS Technologies
Name Value
---- -----
Path C:\Windows\system32;C:\Windows;C:\Windows\System32
\Wbem;C:\
TEMP C:\Users\TOBIAS~1\AppData\Local\Temp
ProgramData C:\ProgramData
PATHEXT .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;
.MSC;.4mm
ALLUSERSPROFILE C:\ProgramData
PUBLIC C:\Users\Public
OS Windows_NT
USERPROFILE C:\Users\Tobias Weltner
HOMEDRIVE C:
(...)
You'll be able to retrieve the information it contains when you've located the appropriate
environment variable and you know its name:
$env:userprofile
C:\Users\Tobias Weltner
Creating 7ew Environment Iariables
You can create completely new environment variables in the same way you create normal variables.
Just specify in which area the variable is to be created, with en$)
$env:TestVar = !2
(ir en4:\t*
Name Value
---- -----
TMP C:\Users\TOBIAS~1\AppData\Local\Temp
TEMP C:\Users\TOBIAS~1\AppData\Local\Temp
TestVar 12
)eleting and 'odi0!ing Environment Iariables
Deleting and modifying environment variables are done in the same way as normal PowerShell
variables. For example, if you'd like to remove the environment variable windir, just delete it from
the en$)drive:
# Environment variable will be deleted:
del en4:\5indir
# Deleted environment variables are no longer available:
$env:windir
You can modify environment variables by simply assigning new variables to them. The next line will
turn your system into an Apple computer-at least to all appearances:
Table of Contents | About PowerShell Plus 71 Sponsors | Resources | BBS Technologies
$env:OS = "Apple MacIntosh OS X"
(ir en4:
Name Value
---- -----
Path C:\Windows\system32;C:\Windows;
C:\Windows\System32\Wbem;C:\
(...)
?5 Apple @ac.ntosh ?5 A
USERPROFILE C:\Users\Tobias Weltner
HOMEDRIVE C:
Aren't these changes dangerous? After all, the environment variables control the entire system.
Fortunately, all changes you make are completely safe and reversible. PowerShell works with a copy
of real environment variables, the so called "process" set. After closing and restarting PowerShell,
the environment variables will return to their previous state. Your changes only affect the current
PowerShell session. Use direct .NET framework methods to change environment variables
persistently. We cover those in a moment.
You can add new folders to the list of trustworthy folders by changing or appending environment
variables. You have read in Chapter 2 that content in trustworthy folders (documents, scripts,
programs) can be launched in PowerShell without having to specify an absolute or relative path
name or even a file extension.
# Create a special folder:
md c:\m<3ool6
# Create an example script in this folder:
" 'Hello!' " > c:\m<3ool6\sayHello.ps1
# Usually, you would have to specify a qualified path name:
C:\m<3ool6\sayHello.ps1
Hello!
# The folder is now added to the path environment:
$env:path += ";C:\myTools"
# All scripts and commands can be started immediately in this
# folder simply by entering their name:
6a<Hello
Hello!
Permanent 'odi0ications o0 Environment Iariables
All changes to environment variables only affect the local copy that your PowerShell session is using.
To make changes permanent, you have two choices. You can either make the changes in one of your
profile scripts, which get executed each time you launch PowerShell or you can use sophisticated
.NET methods directly to change the underlying original environment variables. When you do this
your changes are permanent.
$oldValue = FenvironmentG::GetEnvironmentvariable$"Path", "User"&
Table of Contents | About PowerShell Plus 72 Sponsors | Resources | BBS Technologies
$newValue = ";c:\myTools"
FenvironmentG::SetEnvironmentvariable$"Path", $newValue, "User"&
Access to commands of the .NET Framework as shown in this
example will be described in depth in Chapter 6.
When you close and restart PowerShell, the Path environment variable will now retain the changed
value. You can easily check this:
$env:Path
The permanent change you just made applies only to you, the logged-on user. If you'd like the
change to be in effect for all computer users, replace the B<serB argument by BMachine.B You will
need full administrator privileges to do that.
Change environment variables permanently only when there is no
other way. For most purposes, it is completely sufficient to change
the temporary process set from within PowerShell.
)rive Iariables
When you access variables outside of PowerShell's own variable system (like the environment
variables), the prefix to the variable name really is just the name of the virtual drive that gives
access to the information. Let's take a closer look:
$env:windir
Using this statement, you've just read the contents of the environment variable windir. However, in
reality, en$)windir is a file path and leads to the "file" windir on the en$)drive. So, if you specify a
path name behind "$", this variable will furnish the contents of the specified "file".
)irectl! Accessing "ile Paths
This actually works with (nearly) all drives, even with real data drives. In this case, the direct
variable returns the contents of the actual file. The path must be enclosed in braces because normal
files paths include special characters like ":" and "\", which PowerShell can misinterpret:
$9c:\autoexec.bat:
REM Dummy file for NTVDM
Table of Contents | About PowerShell Plus 73 Sponsors | Resources | BBS Technologies
And there's yet another restriction: the path behind "$" is always interpreted literally. You cannot
use variables or environment variables in it. As a result, the following command would be useless
because PowerShell wouldn't find the file:
$9$env:windir\windowsupdate.log:
This problem could be solved by the cmdlet 4n$oke--(pression. It
executes any kind of command that you pass as a string. Here, you
could assemble the path name and pass it to 4n$oke--(pression:
$command = "`${$env:windir\windowsupdate.log}"
Invoke-Expression $command
The "`" character in front of the first "$", by the way, is not a typo but a
character as it's known as the "backtick" character. You specify it in front of
all characters that normally have a special meaning that you want to override
during the current operation. Without the backtick character, PowerShell
would interpret the contents in the first line as a direct variable and replace it
with the value of the variable. But after a backtick, the "$" character remains
a normal text character.
Why didn't we enclose the text in the first line in simple quotation marks?
Because then PowerShell wouldn't have made any automatic replacements.
The environment variable =en$)windir wouldn't have been resolved, either.
Consequently, you need the backtick character in text whenever you want to
resolve only part of the text.
Direct variables work with most (but not all) drives that 'et-PS.ri$e reports. For example, you
would address the function with your path name to see the definition of a function:
$function:tabexpansion
You can also load functions in Notepad in this way:
$function:tabexpansion > function.ps1B notepad function.ps1
Area allocator )escription
en$) Environment variables
7unction) Functions
$ariable) Variables
Table of Contents | About PowerShell Plus 74 Sponsors | Resources | BBS Technologies
CPath na!eD File system
Table -$%: Variable areas made available by external providers
Ad(hoc Iariables: Sub(Expressions
There are also variables that are never assigned a value in the first place. Instead, the variable
contains an expression. The expression is evaluated and yields the result each time you query the
variable. The code in the parentheses always recalculates the content of this "variable."
$$2+2&
4
Why not just simply write:
$2+2&
4
Or even simpler:
2+2
4
=%?E?& is a variable and, consequently, like all other variables, can be embedded. For example, in
text:
"Result = $(2+2)"
Result = 4
You'll find that ad hoc variables are important once you're working with objects and want to output a
particular object property. We'll discuss objects in more detail later in Chapter 6. Until then, the
following example should make the principle clear:
# Get file:
$file = (ir c:\autoexec.bat
# File size given by length property:
$file.length
# To embed the file size in text, ad hoc variables are required:
"The size of the file is $($file.Length) bytes."
Try this without ad hoc variables. PowerShell would only have replaced =7ile with the value of the
variable and appended ".Length" as static text:
Table of Contents | About PowerShell Plus 75 Sponsors | Resources | BBS Technologies
"The size of the file is $($file.Length) bytes."
3he 6iHe o2 the 2ile i6
C:\autoexec.bat.Length bytes.
Scope o0 Iariables
PowerShell variables can have a "scope" which determines where a variable is available. PowerShell
supports four special variable scopes: lobal, local@ pri$ate, and script. These scopes allow you to
restrict variable visibility in functions or scripts.
Automatic ,estriction
If you don't do anything at all, PowerShell will automatically restrict the visibility of its variables. To
see how this works, create a little test script:
Notepad test1.ps1
Notepad will open. Type the following script, save it, and then close Notepad:
$windows = $env:windir
"Windows Folder: $windows"
Now call your script:
.\test.ps1
If your script doesn't start, script execution may be turned off. By
entering the command Set--(ecutionPolicy Re!oteSined, you can
grant PowerShell permission to run scripts. You'll learn more about
this in Chapter 10.
The script reports the Windows folder. From within the script, the folder path is stored in the variable
=windows. After the script has done its work, take a look to see what variables the script has left
behind: retrieve the variable =windows. It's empty. The variables in your script were defined in a
different scope than the variables within your console and so were isolated from each other.
=windows in the console and =windows in your script are, in fact, completely different variables as
shown by this script:
$windows = "Hello"
.\test1.ps1
$windows
"Hello"
Table of Contents | About PowerShell Plus 76 Sponsors | Resources | BBS Technologies
Although the script in its variable =windows stored other information, the variable =windows in your
console retains its value. PowerShell normally creates its own variable scope for every script and
every function.
Changing Iariable Iisibilit!
You can easily find out how the result would have looked without automatic restrictions on variable
visibility. All you do is type a single dot "." before the script file path to turn off restrictions on
visibility. Type a dot and a space in front of the script:
$windows = "Hello"
. .\test1.ps1
$windows
"C:\Windows"
This time, the variables within the script will take effect on variables in the console. If you launch the
script "dot-sourced," PowerShell won't create new variables for your script. Instead, it uses the
variable scope of the caller. That has advantages and disadvantages that you'll have to weigh
carefully in each application.
Advantage o0 Li0ting Iisibilit! ,estrictions: Clear and
8nambiguous Start Conditions
Imagine an example in which a script creates a read-only variable as a constant. Such variables may
neither be modified nor removed. This won't be a problem if you start the script with scoping
restrictions because the constant is created in the variable scope of the script. The entire variable
scope will be completely disposed of when the script ends. Constants that you create in scripts are
therefore write-protected only within the script. You can create another test script to verify that:
Notepad test2.ps1
Type in it the following code, which creates a read-only constant:
New-Variable a -value ! -option Con6tant
"Value: $a"
Save the test script and close Notepad. The write-protected constant will be created when you start
the script the way you would normally, , but it will also be removed when the script ends. You can
run the script as often as you wish:
.\test2.ps1
Value: 1
.\test2.ps1
Value: 1
Table of Contents | About PowerShell Plus 77 Sponsors | Resources | BBS Technologies
Now try to call the "dot-sourced" script. Because it doesn't include any scoping restrictions anymore,
the constant will not be created in the variable scope of the script, but in the variable scope of the
caller, i.e., the console. The constant will be preserved when the script ends. If you call the script a
second time, it will fail because it can't overwrite the constant that still exists from the script that
was last invoked:
. .\test2.ps1
Value: 1
. .\test2.ps1
+e%-Variable : A #ariable %ith the name "a" already exists.
At ':($sers(Tobias )eltner(test*.ps1:1 char:14
+e%-Variable !!!! a -#alue 1 -option 'onstant
It's interesting that you can still always run the script, despite the existing variable =a@ if you start it
again normally and with its own variable scope:
.\test2.ps1
Value: 1
The script now takes all variables from the caller's variable scope, and so the existing variable =a as
well, but when new variables are created or existing ones modified, this happens exclusively in the
script's own variable scope. Therefore, conflicts are minimized when scoping restriction is active.
This works conversely, too: use the AllScope option if you'd like to
expressly prevent the own variable scope from redefining a variable
from another variable scope. This way, the variable will be copied
automatically to every new variable scope and created there as a
local variable. This enables you to prevent constants from being re-
defined in another variable scope:
# Test function with its own local variable scope tries to
# redefine the variable $setValue:
Function 3e6t 9$setValue = AAB $setValue :
# Read-only variable is created. Test function may modify this
# value nevertheless by creating a new local variable:
New-Variable 6etDal8e -option "ReadOnly" -value 2""
3e6t
99
# Variable is created with the AllScope option and automatically
# copied to local variable scope. Overwriting is now no longer
# possible.
Remove-Variable 6etDal8e -force
New-Variable 6etDal8e -option "ReadOnly,<b>AllScope</b>" -value
Table of Contents | About PowerShell Plus 78 Sponsors | Resources | BBS Technologies
2""
The #ariable "setValue" cannot be o#er%ritten since it is a
constant or read-only.
At line:1 char:*&
1unction Test B:setValue !!!! ; CCD :setValue E
*88
Setting the Scope o0 &ndividual Iariables
Up to now, the governing principle was "all or nothing": either all variables of a function or a script
were private or they were public (global). Now, let's use the scope modifiers pri$ate, local, script,
and lobal.
Scope
allocation
)escription
=pri$ate)tes
t A 1
The variable will be created only in the current scope and not
passed to other scopes. Consequently, it can only be read and
written in the current scope.
=local)test
A 1
Variables will be created only in the local scope. That is the
default for variables that are specified without a scope. Local
variables can be read from scopes originating from the current
scope, but they cannot be modified.
=script)test
A 1
The variable is valid only in a script, but valid everywhere in it.
Consequently, a function in a script can address other variables,
which, while defined in a script, are outside the function.
=lobal)test
A 1
The variable is valid everywhere, even outside functions and
scripts.
Table -$-: Variable scopes and validity of variables
PowerShell automatically creates scopes, even when you first start the PowerShell console. It gets
the first (global) scope. Additional scopes will be added when you use functions and scripts. Every
function and every script acquires its own scope. As long as you work from within the PowerShell
console, there will be only one scope. In this case, all scope allocations will function in exactly the
same way:
Table of Contents | About PowerShell Plus 79 Sponsors | Resources | BBS Technologies
$test = !
$local:test
1
$script:test = !2
$global:test
12
$private:test
12
Create a second scope by defining a function. As soon as you call the function, PowerShell will switch
to the function's own new scope. And now things appear somewhat confusing: which rules apply to
variables and their validity? Let's take a look at what happens to variables that you create in the
scope of the console and then read or modify in the scope of the function:
# Define test function:
Function te6t 9 "variable = $a"B $a = !""" :
# Create variable in console scope and call test function:
$a = !2
3e6t
variable = 12
# After calling test function, control modifications in console scope:
$a
12
When you don't use any special scope allocators, a new scope can read the variables of the old
scope, but not change them. If the new scope modifies a variable from the old scope, as in the
example above, then the modification will be automatically created in a new local variable of the new
scope. The modification has no effect on the old scope.
Is it possible to prevent variables from the old scope from being read by a new scope? The answer is
yes. Variables are pri$ate for the allocator, since the variables that you create with it are not passed
to other scopes. The function then reports "variable = ", because the variable =a is suddenly
invisible to the function.
# Define test function:
Function te6t 9 "variable = $a"B $a = !""" :
# Create variable in console scope and call test function:
:pri#ate:a = !2
3e6t
variable =
# Check variable for modifications after calling test function in console scope:
Table of Contents | About PowerShell Plus 80 Sponsors | Resources | BBS Technologies
$a
12
Only when you create a completely new variable by using =pri$ate)
is it in fact private. If the variable already existed, PowerShell will
not reset the scope of the existing variable. That is (somewhat)
logical, because there is only one scope in the console scope. The
existing variable is found under the pri$ate) allocator and so is not
created again.
To achieve the result you expect, you must either first remove the existing
variable $a using the statement Re!o$e-8ariable a before you create it
again, or manually allocate the status of a private variable to an existing
variable: %'et-8ariable a&.,ptions A BPri$ateB. Also, by using %'et-8ariable
a&.,ptions A B5oneB you can make a variable become a local variable again.
The scope of a variable is disclosed, as shown in Table 3.6, by selecting the
,ptions property.
It works conversely too as the function can also modify the variable in the console scope. That's the
purpose of the lobal) allocator. If it's specified, then the statement changes the variable in all
existing scopes:
# Define test function:
Function te6t 9 "variable = $a"B :global:a = !""" :
# Create variable in console scope and call test function:
Remove-Variable a
$private:a = !2
3e6t
variable =
# After calling test function check variable for modifications
# in console:
$a
1000
The allocator script) works in a very similar way. It makes a variable global inside of a script, but
does not touch variables outside of the script. If you call the function directly from within the
console, then lobal) and script) will supply the same result. But when you use script) from within
PowerShell scripts, you will create variables that are valid everywhere within the script. However,
after termination of the script, will have no effect on the console used to call the script.
Scope 8se
=lobal The variable is valid in all scopes and is preserved when a script or a
Table of Contents | About PowerShell Plus 81 Sponsors | Resources | BBS Technologies
function ends its work.
=script
The variable is valid only within a script, but everywhere within it.
Once the script is executed, the variable is removed.
=pri$ate
The variable is valid only in the current scope, either a script or a
function. It cannot be passed to other scopes.
=local
The variable is valid only in the current scope. All scopes called with
it can read, but not change, the contents of the variable.
Modifications are also stored in new local variables of the current
scope. =local) is the default if you don't specify a particular scope.
Table -$/: Practical usage of scope allocations
Iariable T!pes and <Strongl! T!ping<
Variables store arbitrary information when PowerShell automatically picks the appropriate data type.
You don't have to do anything. However, by appending the command .'et6ype%&.5a!e to a
variable, you can verify the data type that PowerShell has chosen for a variable. You don't even
need the variable. Type the value in parentheses and call .'et6ype%&.5a!e to find out in which data
type PowerShell stores the value:
$!2&.GetType$&.Name
Int32
$!""""""""""""&.GetType$&.Name
Int64
$!2.%&.GetType$&.Name
Double
$!2d&.GetType$&.Name
Decimal
$"H"&.GetType$&.Name
String
$Get-Date&.GetType$&.Name
Table of Contents | About PowerShell Plus 82 Sponsors | Resources | BBS Technologies
DateTime
PowerShell assigns the best-fit pri!iti$e data type for a given value. If a number is too large for a
32-bit integer, it will use a 64-bit integer. If it's a decimal number, then the .ouble data type will be
used. In the case of text, PowerShell uses the Strin data type. Date and time values are stored in
.ate6i!e objects.
This process of automatic selection is called "weakly typed," and while easy, it's also often restrictive
-or even risky. If PowerShell picks the wrong data type, strange things can happen. For example,
let's say a variable should really store the number of files to be copied. If you erroneously assign a
text value instead of a numeric value to this variable, PowerShell will happily store the text, not the
number. The variable type will be automatically modified. This is why professional programmers and
script developers often prefer strongly typed variables that specify the exact type of data to be
stored, rather than delivering error messages when a wrong data type is assigned.
Another reason for a strong type specification: Every data type has its own set of helper functions.
In fact, PowerShell doesn't always select the best data type for a particular value. For example,
date, time and XML, are by default stored as plain text in a Strin data type. This is somewhat
unfortunate, because you'll have to do without many useful date or XML commands that use
specialized .ate6i!e or FML data types. So, in practice, there are two important reasons for you to
set the variable type yourself:
T!pe sa0et!: If you have assigned a type to a variable yourself, then the type will be
preserved no matter what happens and will never be automatically modified. You can be
absolutely sure that a value of the correct type is stored in the variable. If later on someone
were to mistakenly assign a value to the variable that doesn't match the originally chosen
type, this will cause an error message to be delivered.
Special variable t!pes: When automatically assigning a variable type, PowerShell takes into
consideration only general variable types like 4ntG? or Strin. Often, it's appropriate to store
values in a specialized variable type like .ate6i!e in order to be able to use the special
commands and options available for this variable type.
Assigning "ixed T!pes
To assign a particular type to a variable, enclose it in square brackets before the variable name. For
example, if you know that a particular variable should hold only numbers in the range 0 to 255, you
could create this variable explicitly with the 9yte type:
FByteG$flag = !2
$flag.GetType$&.Name
Byte
The variable will now store your contents in a single byte, which is not only very economical, but it
will also flag it with an error if a value outside the permissible range is specified:
$flag = '""
The #alue "488" cannot be con#erted to the type "5ystem.Fyte".
Grror: "The #alue for an unsigned byte %as too large or too small."
At line:1 char:9
Table of Contents | About PowerShell Plus 83 Sponsors | Resources | BBS Technologies
:flag !!!! ; 488
The Advantages o0 Speciali.ed T!pes
There is an additional and important reason to assign data types manually because every data type
has its own set of special commands. For example, a date can be stored as text in a Strin data
type. And that's just exactly what PowerShell does: it's not clever enough to automatically guess
that this really is a date or time:
$date = "November 12, 2004"
$date
November 12, 2004
If you store a date as Strin, then you'll have no access to special date functions. Only .ate6i!e
objects make them available. So, if you're working with date and time indicators, it's better to store
them explicitly as .ate6i!e:
FdatetimeG$date = "November 12, 2004"
$date
Friday, November 12, 2004 00:00:00
The output of the variable will now immediately tell you the day of the week corresponding to the
date, and also enable comprehensive date and time calculation commands. That makes it easy, for
example, to find the date 60 days later:
$date.AddDays$6"&
Tuesday, January 11, 2005 00:00:00
PowerShell supports all the usual .NET variable types that you find in Table 3.5. XML documents can
be much better processed using the FML data type then the standard Strin data type:
# PowerShell stores a text in XML format as a string:
$t = "<servers><server name='PC1' ip='10.10.10.10'/>" +
"<server name='PC2' ip='10.10.10.12'/></servers>"
$t
<servers><server name='PC1' ip='10.10.10.10'/>
<server name='PC2' ip='10.10.10.12'/></servers>
# If you assign the text to a data type[xml], you'll
# suddenly be able to access the XML structure:
FxmlG$list = $t
$list.servers
server
------
{PC1, PC2}
Table of Contents | About PowerShell Plus 84 Sponsors | Resources | BBS Technologies
$list.servers.server
name ip
---- --
PC1 10.10.10.10
PC2 10.10.10.12
# Even changes to the XML contents are possible:
$list.servers.serverF"G.ip = "10.10.10.11"
$list.servers
name ip
---- --
PC1 10.10.10.11
PC2 10.10.10.12
# The result could be output again as text, including the
# modification:
$list.get_InnerXML$&
<servers><server name="PC1" ip="10.10.10.11" />
<server name="PC2" ip="10.10.10.12" /></servers>
Iariable
t!pe
)escription Example
[array] An array
[bool] Yes-no value [boolean]$flag = $true
[byte] Unsigned 8-bit integer, 0...255 [byte]$value = 12
[char] Individual unicode character [char]$a = "t"
[datetime] Date and time indications
[datetime]$date =
"12.Nov 2004 12:30"
[decimal] Decimal number
[decimal]$a = 12
$a = 12d
[double]
Double-precision floating point
decimal
$amount = 12.45
[guid] Globally unambiguous 32-byte [guid]$id =
Table of Contents | About PowerShell Plus 85 Sponsors | Resources | BBS Technologies
identification number
[System.Guid]::NewGuid(
)
$id.toString()
[hashtable] Hash table
[int16] 16-bit integer with characters [int16]$value = 1000
[int32],
[int]
32-bit integers with characters [int32]$value = 5000
[int64],
[long]
64-bit integers with characters [int64]$value = 4GB
[nullable]
Widens another data type to include
the ability to contain null values. It
can be used, among others, to
implement optional parameters
[Nullable``1[[System.Dat
eTime]]]$test = Get-Date
$test = $null
[psobject] PowerShell object
[regex] Regular expression
$text = "Hello World"
[regex]::split($text, "lo")
[sbyte] 8-bit integers with characters [sbyte]$value = -12
[scriptblock] PowerShell scriptblock
[single],
[float]
Single-precision floating point
number
[single]$amount = 44.67
[string] String [string]$text = "Hello"
[switch] PowerShell switch parameter
[timespan] Time interval
[timespan]$t = New-
TimeSpan $(Get-Date)
"1.Sep 07"
[type] Type
Table of Contents | About PowerShell Plus 86 Sponsors | Resources | BBS Technologies
[uint16] Unsigned 16-bit integer [uint16]$value = 1000
[uint32] Unsigned 32-bit integer [uint32]$value = 5000
[uint64] Unsigned 64-bit integer [uint64]$value = 4GB
[xml] XML document
Table -$1: Variable types
Iariable 'anagement: 4ehind the Scenes
Whenever you create a new variable in PowerShell, it will be stored "behind the scenes" in a
PS8ariable object. This object contains not just the value of the variable, but also other information,
such as the description that you assigned to the variable or additional options like write-protection.
If you retrieve a variable in PowerShell, PowerShell will return only the variable value. If you'd like to
see the remaining information that was assigned to the variable, you'll need the underlying
PS8ariable object. 'et-8ariable will get it to you:
$testvariable = "Hello"
$psvariable = Get-Variable te6t4aria,le
You can now display all the information about =test$ariable by outputting =ps$ariable. To see all
object properties and not just the default properties, pipe the output to the cmdlet /or!at-List:
$psvariable | Format-List
Name : testvariable
Description :
Value : Hello
Options : None
Attributes : {}
)escription: The description you specified for the variable.
Ialue: the value assigned currently to the variable (i.e. its contents).
*ptions: Options that have been set such as write-protection or AllScope.
Attributes: Additional features, such as permitted data type of a variable for strongly typed
variables. The brackets behind Attributes indicate that this is an array, which can consist of
several values that can be combined with each other.
Table of Contents | About PowerShell Plus 87 Sponsors | Resources | BBS Technologies
SubseKuent 'odi0ication o0 Iariables *ptions
One reason for dealing with the PS8ariable object of a variable is to modify the variable's settings.
Use either the cmdlet Set-8ariable or directly modify the PS8ariable object. For example, if you'd
like to change the description of a variable, get the appropriate PS8ariable object and modify its
.escription property:
# Create new variable:
$test = "New variable"
# Create PSVariable object:
$psvariable = Get-Variable te6t
# Modify description:
$psvariable.Description = "Subsequently added description"
(ir 4aria,le:\te6t | Format-Table name, de6cription
Name Description
---- -----------
test Subsequently added description
# Get PSVariable object and directly modify the description:
$Get-Variable te6t&.Description =
"An additional modification of the description."
(ir 4aria,le:\te6t | Format-Table name, de6cription
Name Description
---- -----------
test An additional modification of the description.
# Modify a description of an existing variable with Set-Variable:
Set-Variable te6t -description "Another modification"
(ir 4aria,le:\te6t | Format-Table name, de6cription
Name Description
---- -----------
test Another modification
As you can see in the example above, you do not need to store the PS8ariable object in its own
variable to access its .escription property. Instead, use a sub-expression, i.e. a statement in
parentheses. PowerShell will then evaluate the contents of the sub-expression separately. The
expression directly returns the required PS8ariable object so you can then call the .escription
property directly from the result of the sub-expression. You could have done the same thing by
using Set-8ariable. Reading the settings works only with the PS8ariable object:
$Get-Variable te6t&.Description
An additional modification of the description.
Table of Contents | About PowerShell Plus 88 Sponsors | Resources | BBS Technologies
Activating 3rite(Protection
You can manipulate other variable properties, too. For example, if you'd like to write-protect a
variable, do this:
$Example = !"
# Put option directly in PSVariable object:
$Get-Variable 0-ample&.Options = "ReadOnly"
# Modify option as wish with Set-Variable; because the variable
# is read-only, -force is required:
Set-Variable 0-ample -option "None" -force
# Write-protection turned off again; variable contents may now
# be modified freely:
$Example = 2"
The "onstant option must be set when a variable is created because you may not convert an
existing variable into a constant.
# A normal variable may not be converted into a constant:
$constant = !2'#%
$Get-Variable con6tant&.Options = "Constant"
Gxception in setting "?ptions": "The existing #ariable "constant"
may not be set as a constant. Variables may only be set as
constants %hen they are created."
At line:1 char:*9
H,et-Variable constantI.? !!!! options ; "'onstant"
The remaining two options, Pri$ate and AllScope, are the basis for local and global variables as they
can then be extracted using the method described above.
*ption )escription
B5oneB NO option (default)
BRead,nly
B
Variable contents may only be modified by means of the -7orce
parameter
B"onstant
B
Variable contents can't be modified at all. This option must already
be specified when the variable is created. Once specified this
option cannot be changed.
BPri$ateB The variable is visible only in a particular context (local variable).
BAllScopeB The variable is automatically copied in a new variable scope.
Table of Contents | About PowerShell Plus 89 Sponsors | Resources | BBS Technologies
Table -$2: Options of a PowerShell variable
T!pe Speci0ication o0 Iariables
PowerShell stores the strict data type of a variable in the Attributes property if you specified a
special type. As long as Attributes is empty, the variable will store any type of data and PowerShell
will automatically select the appropriate data type.
Once you assign a fixed data type to a variable, the data type will be stored in the Attributes
property, setting the variable to the assigned data type. If you delete the Attributes property, the
variable will be un-typed again and stores all kinds of data:
# List attributes and delete:
$Get-Variable a&.Attributes
TypeId
------
System.Management.Automation.ArgumentTypeConverterAttribute
# Delete type specification:
$Get-Variable a&.Attributes.Clear$&
# Strong type specification is removed; now the variable can
# store text again:
$a = "Test"
Ieri0!ing and Ialidating Iariable Contents
The Attributes property of a PS8ariable object can include additional conditions, such as the
maximum length of a variable. In the following example, a valid length from 2 to 8 characters is
assigned to a variable. An error will be generated if you try to store text that is shorter than 2
characters or longer than 8 characters:
$a = "Hello"
$aa = Get-Variable a
$aa.Attributes.Add$$$New-Object `
System.Management.Automation.ValidateLengthAttribute `
-argumentList 2,I&&
$a = "Permitted"
$a = "Prohibited because its length is not from 2 to 8 characters"
Fecause of an in#alid #alue #erification H/rohibited because
its length is not from * to 8 charactersI may not be carried out for
the #ariable "a".
At line:1 char:4
:a !!!! ; "/rohibited because its length is not from * to 8
In the above example Add%& added a new .NET object to the attributes with 5ew-,b>ect. You'll learn
more about 5ew-,b>ect in Chapter 6. Along with 8alidateLenthAttribute, there are additional
restrictions that you can place on variables.
Table of Contents | About PowerShell Plus 90 Sponsors | Resources | BBS Technologies
,estriction Categor!
Variable may not be zero 8alidate5ot5ullAttribute
Variable may not be zero or empty
8alidate5ot5ull,r-!ptyAttribut
e
Variable must match a Regular Expression 8alidatePatternAttribute
Variable must match a particular number
range
8alidateRaneAttribute
Variable may have only a particular set value 8alidateSetAttribute
Table -$5: Available variable validation classes
In the following example, the variable must contain a valid e-mail address or all values not matching
an e-mail address will generate an error. The e-mail address is defined by what is called a Regular
Expression. You'll learn more about Regular Expressions in Chapter 13.
$email = "[email protected]"
$v = Get-Variable email
$pattern = "\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b"
$v.Attributes.Add$$$New-Object `
System.Management.Automation.ValidatePatternAttribute `
-argumentList $pattern&&
$email = "[email protected]"
$email = "invalid@email"
Fecause of an in#alid #alue #erification Hin#alidJemailI may not
be carried out for the #ariable "email".
At line:1 char:&
:email !!!! ; "in#alidJemail"
If you want to assign a set number range to a variable, use 8alidateRaneAttribute. The variable
=ae accepts only numbers from 5 to 100:
$age = !I
$v = Get-Variable age
$v.Attributes.Add$$$New-Object `
System.Management.Automation.ValidateRangeAttribute `
-argumentList %,!""&&
$age = '"
$age = !!"
Table of Contents | About PowerShell Plus 91 Sponsors | Resources | BBS Technologies
Fecause of an in#alid #alue #erification H118I may not be
carried out for the #ariable "age".
At line:1 char:&
:age !!!! ; 118
If you would like to limit a variable to special key values, 8alidateSetAttribute is the right option. The
variable =option accepts only the contents yes, no, or perhaps:
$option = "yes"
$v = Get-Variable option
$v.Attributes.Add$$$New-Object `
System.Management.Automation.ValidateSetAttribute `
-argumentList "yes", "no", "perhaps"&&
$option = "no"
$option = "perhaps"
$option = "don't know"
Verification cannot be performed because of an in#alid #alue
Hdon0t Kno%I for the #ariable "option".
At line:1 char:8
:option !!!! ; "don0t Kno%"
The validations that you applied to variables in the above example
were originally designed for cmdlets, but you can also use them for
variables as well.
If you'd like to find out more about the parameters that a cmdlet accepts,
you should simply examine the attribute of the cmdlet parameter and look for
validation entries. The following example examines all parameters of the 'et-
"hild4te! cmdlet and takes a closer look at the range of permitted values of
the -,ut9u77er parameter:
# Output all parametersets:
$Get-Command Get-ChildItem&.ParameterSets
(...)
# Output names of parametersets:
$Get-Command Get-ChildItem&.ParameterSets |
ForEach-Object 9 $_.Name :
Items
LiteralItems
# List all parameters of all parametersets:
$Get-Command Get-ChildItem&.ParameterSets |
ForEach-Object 9 $_.Parameters : | ForEach-Object 9 $_.Name :
# Select one parameter:
$parameter = $Get-Command Get-ChildItem&.ParameterSets |
ForEach-Object 9 $_.Parameters : |
Table of Contents | About PowerShell Plus 92 Sponsors | Resources | BBS Technologies
Where-Object 9 $_.Name -eq "OutBuffer" : |
Select-Object -first !
$parameter
Name : OutBuffer
ParameterType : System.Int32
IsMandatory : False
IsDynamic : False
Position : -2147483648
ValueFromPipeline : False
ValueFromPipelineByPropertyName : False
ValueFromRemainingArguments : False
HelpMessage :
Aliases : {ob}
Attributes :
{System.Management.Automation.
AliasAttribute,
__AllParameter
Sets,System.Management.Auto
mat
ion.ValidateRangeAttribute}
# Determine permitted values:
$parameter.Attributes |
Where-Object 9 $_.TypeId -match "ValidateRangeAttribute" :
MinRange MaxRange TypeId
-------- -------- ------
0 2147483647 System.Management.Automat...
Summar!
Variables store any information. The variable name always begins with the dollar sign "$". The
variable name can consist of numbers, characters, and special characters like the underline
character "_". Variables are not case-sensitive. If you'd like to use characters in variable names with
special meaning to PowerShell (like parenthesis), the variable name must be enclosed in braces.
PowerShell doesn't require that variables be specifically created or declared before use.
Aside from the variables that you create yourself, there are predefined variables that PowerShell
creates called "automatic variables." These variables function like self-defined variables, but they
already include useful key system data or configuration data for PowerShell.
PowerShell always stores variables internally in a PS8ariable object. For example, it contains settings
that write-protect a variable or attach a description to it (Table 3.6). It's easiest for you to activate
this special function by using the 5ew-8ariable or Set-8ariable cmdlets (Table 3.1).
Table of Contents | About PowerShell Plus 93 Sponsors | Resources | BBS Technologies
By default, variables store any values you want. PowerShell automatically ensures that the variable
type matches the value. If you'd like to set variables to a particular variable type ("strong type
specification"), specify the desired type (Table 3.5) in square brackets before the variable name.
Then the variable will store only the values that match the type. In addition, the variable will now
enable the special commands associated with the variable type, such as date manipulation and math
with the .ate6i!e variable type.
Every variable is created in a fixed scope, which PowerShell uses to determine the valid scope of a
variable. When PowerShell starts, an initial variable scope is created, and every script and every
function receive their own respective scope. You may specify a special scope by typing the name of
the desired scope before the variable name and separating it with a colon from the variable name.
You can use the local), pri$ate), script), and lobal) scopes, to address local and global variables. In
addition, further providers can make their own scopes available, which enable you to address their
information just like normal variables. For example, environment variables, which can be accessed
through en$) (Table 3.2).
Finally, direct variables are special variable types. Variable names determine their values. Either a
valid file path is specified as a valid file path, and the variable outputs the contents of this data
object, or the variable name consists of PowerShell code in parentheses. PowerShell then
recalculates the respective "contents" of the variable.
Table of Contents | About PowerShell Plus 94 Sponsors | Resources | BBS Technologies
CHAPTER 4.
Arrays and Hash Tables
Arrays and Hash Tables
No matter how many results a command returns, you can always store the results in a variable
because of a clever trick. PowerShell automatically wraps results into an array when there is more
than one result. In this chapter, you'll learn how arrays work.
You'll also discover a special type of array, a hash table. While normal arrays use a numeric index to
access their elements, hash tables use key-value-pairs.
Topics Covered:
PowerShell Commands Return Arrays
Storing Results in Arrays
Further Processing of Array Elements in a Pipeline
Working with Real Objects
Creating New Arrays
Polymorphic Arrays
Arrays With Only One (Or No) Element
Addressing Array Elements
Choosing Several Elements from an Array
Adding Elements to an Array and Removing Them
Using Hash Tables
Creating a New Hash Table
Storing Arrays in Hash Tables
Inserting New Keys in an Existing Hash Table
Modifying and Removing Values
Using Hash Tables for Output Formatting
Copying Arrays and Hash Tables
Strongly Typed Arrays
Summary
PowerShell Commands ,eturn Arra!s
If you store the result of a command in a variable and then output it, you might at first think that
the variable contains plain text:
$a = ipcon2ig
$a
Windows IP Configuration
Ethernet adapter LAN Connection
Media state
. . . . . . . . . . . : Medium disconnected

Connection-specific DNS Suffix:
Connection location IPv6 Address . : fe80::6093:8889:257e:8d1%8
Table of Contents | About PowerShell Plus 95 Sponsors | Resources | BBS Technologies
IPv4 address . . . . . . . . . . . : 192.168.1.35
Subnet Mask . . . . . . . . . . . . : 255.255.255.0
Standard Gateway . . . . . . . . . . : 192.168.1.1
However, that's not true. Each line is stored as a separate value in your variable and the variable is
really an array. This happens automatically whenever a command returns more than one result.
Storing ,esults in Arra!s
This is how you identify arrays:
$a = "Hello"
$a -is FArrayG
False
$a = ipcon2ig
$a -is FArrayG
True
If the result is an array, you can find the number of elements stored in it by using the "ount
property:
$a.Count
53
In this example, the ipcon7i command returned 53 single results that are all stored in =a. If you'd
like to examine a single array element, specify its index number. If an array has 53 elements, its
valid index numbers are 0 to 52 (the index always starts at 0).
# Show the second element:
$aF!G
Windows IP Configuration
Whether or not the result is an array depends on the number of
results that were returned. If more than one, PowerShell returns
an array. Otherwise, it returns the result directly so the same
command can behave differently from case to case, depending on
the number of results.
$result = (ir
$result -is FarrayG
True
Table of Contents | About PowerShell Plus 96 Sponsors | Resources | BBS Technologies
$result = (ir C:\autoexec.bat
$result -is FarrayG
False
Use the construct H%& if you'd like to force a command to always return its
result in an array. This way the command will always return an array, even if
the command returns only one result or none at all. This way you find out the
number of files in a folder:
$result = ?$(ir&
$result.Count
Or in a line:
?$(ir&.Count
"urther Processing o0 Arra! Elements in a Pipeline
4pcon7i returns each line of text as array, enabling you to process them individually:
# Store result of an array and then pass along a pipeline to Select-String:
$result = ipcon2ig
$result | Select-String "Address"

Connection location IPv6 Address . . . : fe80::6093:8889:257e:8d1%8
IPv4 address . . . . . . . . . . . : 192.168.1.35
Connection location IPv6 Address . : fe80::5efe:192.168.1.35%16

Connection location IPv6 Address . . . : fe80::14ab:a532:a7b9:cd3a%11
# Everything in one line: output only lines including the
# word "address":
ipcon2ig | Select-String "Address"

Connection location IPv6 Address . . . : fe80::6093:8889:257e:8d1%8
IPv4-Adress . . . . . . . . . . . . : 192.168.1.35
Connection location IPv6 Address . . . : fe80::5efe:192.168.1.35%16
Connection location IPv6 Address . . . : fe80::14ab:a532:a7b9:cd3a%11
The result of ipcon7i was passed to Select-Strin@which is a text filter that allows only text lines that
include the searched word through the PowerShell pipeline. With minimal effort, you can reduce the
results of ipcon7i to the information you find relevant.
Table of Contents | About PowerShell Plus 97 Sponsors | Resources | BBS Technologies
3orking with ,eal *bJects
4pcon7i is a legacy command, not a modern PowerShell cmdlet. While it is a command that returns
individual information stored in arrays, this individual information consists of text. Real PowerShell
cmdlets return rich objects, not text, even though this is not apparent at first:
(ir

Directory: Microsoft.PowerShell.Core\FileSystem::C:\Users\
Tobias Weltner
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 10/01/2007 16:09 Application Data
d---- 07/26/2007 11:03 Backup
d-r-- 04/13/2007 15:05 Contacts
d---- 06/28/2007 18:33 Debug
d-r-- 10/04/2007 14:21 Desktop
d-r-- 10/04/2007 21:23 Documents
d-r-- 10/09/2007 12:21 Downloads
(...)
Let's check if the return value is an array:
$result = (ir
$result.Count
82
Every element in an array represents a file or a directory. So if you output an element from the
array to the console, PowerShell automatically converts the object back into text:
# Access the fifth element:
$resultF#G

Directory: Microsoft.PowerShell.Core\FileSystem::C:\Users\
Tobias Weltner
Mode LastWriteTime Length Name
---- ------------- ------ ----
d-r-- 04.10.2007 14:21 Desktop
You will realize that each element is much more than plain text when you pass it to the Format-List
cmdlet and use an asterisk to see all of its properties:
# Display all properties of this element:
$resultF#G | Format-List *
PSPath : Microsoft.PowerShell.Core\FileSystem::
C:\Users\Tobias Weltner\Desktop
PSParentPath : Microsoft.PowerShell.Core\FileSystem::
Table of Contents | About PowerShell Plus 98 Sponsors | Resources | BBS Technologies
C:\Users\Tobias Weltner
PSChildName : Desktop
PSDrive : C
PSProvider : Microsoft.PowerShell.Core\FileSystem
PSIsContainer : True
Mode : d-r--
Name : Desktop
Parent : Tobias Weltner
Exists : True
Root : C:\
FullName : C:\Users\Tobias Weltner\Desktop
Extension :
CreationTime : 04/13/2007 01:54:53
CreationTimeUtc : 04/12/2007 23:54:53
LastAccessTime : 10/04/2007 14:21:20
LastAccessTimeUtc : 10/04/2007 12:21:20
LastWriteTime : 10/04/2007 14:21:20
LastWriteTimeUtc : 10/04/2007 12:21:20
Attributes : ReadOnly, Directory
You'll learn more about these types of objects in Chapter 5.
Creating 7ew Arra!s
You can create your own arrays, too. The easiest way is to use the comma operator:
$array = !,2,',#
$array
1
2
3
4
Specify the single elements that you want to store in the array and then separate them by a comma.
There's even a special shortcut for sequential numbers:
$array = !..#
$array
1
2
3
4
Table of Contents | About PowerShell Plus 99 Sponsors | Resources | BBS Technologies
Pol!morphic Arra!s
Just like variables, individual elements of an array can store any type of value you assign. This way,
you can store whatever you want in an array, even a mixture of different data types. You can
separate the elements using commas:
$array = "Hello", "World", !, 2, $Get-Date&
$array
Hello
World
1
2
Tuesday, August 21, 2007 12:12:28
Why is the 'et-.ate cmdlet in the last example enclosed in
parentheses? Just try it without parentheses. Arrays can only store
data. 'et-.ate is a command and no data. Since you want
PowerShell to evaluate the command first and then put its result
into the array, you need to use parentheses. Parentheses identify a
sub-expression and tell PowerShell to evaluate and process it first.
Arra!s 3ith *nl! *ne L*r 7oM Element
How do you create arrays with just one single element? Here's how:
$array = ,!
$array.Length
1
You'll need to use the construct H%...&to create an array without any elements at all:
$array = ?$&
$array.Length
0
$array = ?$!2&
$array
12
$array = ?$!,2,',"Hello"&
$array
Table of Contents | About PowerShell Plus 100 Sponsors | Resources | BBS Technologies
1
2
3
Hello
Addressing Arra! Elements
Every element in an array is addressed using its index number. Negative index numbers count from
last to first. You can also use expressions that calculate the index value:
# Create your own new array:
$array = -%..!2
# Access the first element:
$arrayF"G
-5
# Access the last element (several methods):
$arrayF-!G
12
$arrayF$array.Count-!G
12
$arrayF$array.length-!G
12
# Access a dynamically generated array that is not stored in a variable:
$-%..!2&F2G
-3
Remember, the first element in your array always has the index number 0. The index -1 will always
give you the last element in an array. The example demonstrates that the total number of all
elements will be returned in two properties: "ount and Lenth. Both of these properties will behave
identically.
Choosing Several Elements 0rom an Arra!
You can use square brackets to select multiple elements in an array. In doing that, you get a new
array containing only the selected elements from the old array:
# Store directory listing in a variable:
$list = dir
# Output only the 2nd, 5th, 8th, and 13th entry:
Table of Contents | About PowerShell Plus 101 Sponsors | Resources | BBS Technologies
$listF!,#,*,!2G
Directory: Microsoft.PowerShell.Core\FileSystem::C:\Users\
Tobias Weltner
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 07/26/2007 11:03 Backup
d-r-- 08/20/2007 07:52 Desktop
d-r-- 08/12/2007 10:21 Favorites
d-r-- 04/13/2007 01:55 Saved Games
The second line selects the second, fifth, eighth, and thirteenth elements (remember that the index
begins at 0). You can use this approach to reverse the contents of an array:
# Create an array with values from 1 to 10
$array = !..!"
# Select the elements from 9 to 0 (output array contents
# in reverse order):
$array = $arrayF$$array.length-!&.."G
$array
10
9
...
1
Reversing the contents of an array using the approach (described
above) is not particularly efficient because PowerShell has to store
the result in a new array. Instead, you should use the special array
functions of the .NET Framework (see Chapter 6). They enable you
to reverse the contents of an array very efficiently:
# Create an array containing text and output contents:
$a = ipcon2ig
$a
# Reverse array contents and then output it again:
FarrayG::Reverse$$a&
$a
Adding Elements to an Arra! and ,emoving Them
Arrays always contain a fixed number of elements. You'll have to make a new copy of the array with
a new size to add or remove elements later. You can simply use the "+=" operator to do that and
then add new elements to an existing array:
# Add a new element to an existing array:
$array += "New Value"
Table of Contents | About PowerShell Plus 102 Sponsors | Resources | BBS Technologies
$array
1
2
3
New Value
Array sizes can't be modified so PowerShell will work behind the scenes to create a brand-new,
larger array, copying the contents of the old array into it, and adding the new element. PowerShell
works exactly the same way when you want to delete elements from an array. Here, too, the original
array is copied to a new, smaller array while disposing of the old array. For example, the next line
removes elements 4 and 5 using the indexes 3 and 4:
$array = $arrayF"..2G + $arrayF%..!"G
$array.Count
9
8sing +ash Tables
Hash tables store "key-value pairs." So, in hash tables you do not use a numeric index to address
individual elements, but rather the key you assigned to a value.
Creating a 7ew +ash Table
To create a new hash table, use HIJ instead of H%&, and specify the key-value pair that is to be
stored in your new hash table. Use semi-colons to separate key-value pairs:
# Create a new hash table with key-value pairs
$list = ?9Name = "PC01"B 1="10.10.10.10"B J6er="Tobias Weltner":
Name Value
---- -----
Name PC01
IP 10.10.10.10
User Tobias Weltner
# Access to the key "IP" returns the assigned value:
$listF"IP"G
10.10.10.10
# As for arrays, several elements can be selected at the same time:
$listF"Name", "IP"G
PC01
10.10.10.10
Table of Contents | About PowerShell Plus 103 Sponsors | Resources | BBS Technologies
# A key can also be specified by dot notation:
$list.IP
10.10.10.10
# A key can even be stored in a variable:
$key = "IP"
$list.$key
10.10.10.10
# Keys returns all keys in the hash table:
$list.keys
Name
IP
User
# If you combine this, you can output all values in the hash table
$listF$list.keysG
PC01
10.10.10.10
Tobias Weltner
The example shows that you retrieve the values in the hash table using the assigned key. There are
two forms of notation you can use to do this:
SKuare brackets: -ither you use square brackets, like in arrays;
)ot notation: ,r you use dot notation, like with objects, and specify respectively the key
name with the value you want to return. The key name can be specified as a variable.
The square brackets can return several values at the same time exactly like arrays if you specify
several keys and separate them by a comma. Note that the key names in square brackets must be
enclosed in quotation marks (you don't have to do this if you use dot notation).
Storing Arra!s in +ash Tables
You can store classic array inside of hash tables, too. This is possible because hash tables use the
semi-colon as key-value-pair separators, leaving the comma available to create classic arrays:
# Create hash table with arrays as value:
$test = ?9 4al8e! = !2B 4al8e2 = !,2,' :
# Return values (value 2 is an array with three elements):
$test.value1
12
$test.value2
Table of Contents | About PowerShell Plus 104 Sponsors | Resources | BBS Technologies
1
2
3
&nserting 7ew e!s in an Existing +ash Table
If you'd like to insert new key-value pairs in an existing hash table, just specify the new key and the
value that is to be assigned to the new key. Again, you can choose between the square brackets and
dot notations.
# Create a new hash table with key-value pairs
$list = ?9Name = "PC01"B 1="10.10.10.10"B J6er="Tobias Weltner":
# Insert two new key-value pairs in the list (two different
# notations are possible):
$list.Date = Get-Date
$listF"Location"G = "Hanover"
# Check result:
$list
Name Value
---- -----
Name PC01
Location Hanover
Date 08/21/2007 13:00:18
IP 10.10.10.10
User Tobias Weltner
Because it's easy to insert new keys in an existing hash table you can create empty hash tables and
then insert keys as needed:
# Create empty hash table
$list = ?9:
# Subsequently insert key-value pairs when required
$list.Name = "PC01"
$list.Location = "Hanover"
$...&
'odi0!ing and ,emoving Ialues
If all you want to do is to change the value of an existing key in your hash table, just overwrite the
value:
# Overwrite the value of an existing key with a new value (two possible notations):
$listF"Date"G = $Get-Date&.AddDays$-!&
$list.Location = "New York"
Name Value
---- -----
Name PC01
Table of Contents | About PowerShell Plus 105 Sponsors | Resources | BBS Technologies
Location New York
Date 08/20/2007 13:10:12
IP 10.10.10.10
User Tobias Weltner
If you'd like to completely remove a key from the hash table, use Re!o$e%& and as an argument
specify the key that you want to remove:
$list.remove$"Date"&
8sing +ash Tables 0or *utput "ormatting
An interesting use for hash tables is to format text. Normally, PowerShell outputs the result of most
commands as a table and internally uses the cmdlet /or!at-6able:
# Both lines return the same result:
(ir
(ir | Format-Table
If you use /or!at-6able, you can pass it a hash table with formatting specifications. This enables
you to control how the result of the command is formatted.
Every column is defined with its own hash table. In the hash table, values are assigned to the
following four keys:
Expression: The name of object property to be displayed in this column
3idth: Character width of the column
Label: Column heading
Alignment: Right or left justification of the column
All you need to do is to pass your format definitions to /or!at-6able to ensure that your listing
shows just the name and date of the last modification in two columns:
# Setting formatting specifications for each column in a hash table:
$column1 = ?9e-pre66ion="Name"B 5idth='"B `
la,el="filename"B alignment="left":
$column2 = ?9e-pre66ion="LastWriteTime"B 5idth=#"B `
la,el="last modification"B alignment="right":
# Output contents of a hash table:
$column1
Name Value
---- -----
alignment left
label File name
width 30
expression Name
# Output Dir command result with format table and
# selected formatting:
Table of Contents | About PowerShell Plus 106 Sponsors | Resources | BBS Technologies
(ir | Format-Table $column1, $column2
File Name Last Modification
--------- ---------------
Application Data 10/1/2007 16:09:57
Backup 07/26/2007 11:03:07
Contacts 04/13/2007 15:05:30
Debug 06/28/2007 18:33:29
Desktop 10/4/2007 14:21:20
Documents 10/4/2007 21:23:10
(...)
You'll learn more about format cmdlets like /or!at-6able in the Chapter 5.
Cop!ing Arra!s and +ash Tables
Copying arrays or hash tables from one variable to another works, but may produce unexpected
results. The reason is that arrays and hash tables are not stored directly in variables, which always
store only a single value. When you work with arrays and hash tables, you are dealing with a
re7erence to the array or hash table. So, if you copy the contents of a variable to another, only the
reference will be copied, not the array or the hash table. That could result in the following
unexpected behavior:
$array1 = !,2,'
$array2 = $array1
$array2F"G = AA
$array1F"G
99
Although the contents of =array? were changed in this example, this affects =array1 as well,
because they are both identical. The variables =array1 and =array? internally reference the same
storage area. Therefore, you have to create a copy if you want to copy arrays or hash tables,:
$array1 = !,2,'
$array2 = $array1.Clone$&
$array2F"G = AA
$array1F"G
1
Whenever you add new elements to an array (or a hash table) or remove existing ones, a copy
action takes place automatically in the background and its results are stored in a new array or hash
table. The following example clearly shows the consequences:
# Create array and store pointer to array in $array2:
$array1 = !,2,'
$array2 = $array1
# Assign a new element to $array2. A new array is created in the process and stored
in $array2:
Table of Contents | About PowerShell Plus 107 Sponsors | Resources | BBS Technologies
$array2 += #
$array2F"G=AA
# $array1 continues to point to the old array:
$array1F"G
1
Strongl! T!ped Arra!s
Arrays are typically polymorphic: you can store any type of value you want in any element.
PowerShell automatically selects the appropriate type for each element. If you want to limit the type
of data that can be stored in an array, use "strong typing" and predefine a particular type. You
should specify the desired variable type in square brackets. You also specify an open and closed
square bracket behind the variable type because this is an array and not a normal variable:
# Create a strongly typed array that can store whole numbers only:
FintFGG$array = !,2,'
# Everything that can be converted into a number is allowed
# (including strings):
$array += #
$array += !2.%6
$array += "123"
# If a value cannot be converted into a whole number, an error
# will be reported:
$array += "Hello"
The #alue "Lello" cannot be con#erted into the type "5ystem..nt4*".
Grror: ".nput string %as not in a correct format."
At line:1 char:9
:array !!!! ; "Lello"
In the example, =array was defined as an array of the 4nteer type. Now, the array is able to store
only whole numbers. If you try to store values in it that cannot be turned into whole numbers, an
error will be reported.
Summar!
Arrays and hash tables can store as many separate elements as you like. Arrays assign a sequential
index number to elements that always begin at 0. Hash tables in contrast use a key name. That's
why every element in hash tables consists of a key-value pair.
You create new arrays with H%-le!ent1@ -le!ent?@ ...&. You can also leave out H%& for arrays and
only use the comma operator. You create new hash tables with HIkey1A$alue1Kkey?A$alue?K ...&.
@{} must always be specified for hash tables. Semi-colons by themselves are not sufficient to
create a new hash table.
Table of Contents | About PowerShell Plus 108 Sponsors | Resources | BBS Technologies
You can address single elements of an array or hash able by using square brackets. Specify either
the index number (for arrays) or the key (for hash tables) of the desired element in the square
brackets. Using this approach you can select and retrieve several elements at the same time.
Table of Contents | About PowerShell Plus 109 Sponsors | Resources | BBS Technologies
CHAPTER 5.
The PowerShell Pipeline
The PowerShell Pipeline
The PowerShell pipeline chains together a number of commands similar to a production assembly.
So, one command hands over its result to the next, and at the end, you receive the result.
Topics Covered:
Using the PowerShell Pipeline
Object-oriented Pipeline
Text Not Converted Until the End
Table 5.1: Typical pipeline cmdlets and functions
Streaming: Real-time Processing or Not?
"Blocking" Pipeline Commands
Converting Objects into Text
Making Object Properties Visible
Formatting Pipeline Results
Displaying Particular Properties
Using Wildcard Characters
Scriptblocks and "Synthetic" Properties
Changing Column Headings
Optimizing Column Width
PropertySets and Views
Sorting and Grouping Pipeline Results
Sort Object and Hash Tables
Grouping Information
Using Grouping Expressions
Using Formatting Cmdlets to Form Groups
Filtering Pipeline Results
Filtering Objects Out of the Pipeline
Selecting Object Properties
Limiting Number of Objects
Processing All Pipeline Results Simultaneously
Removing Doubles
Analyzing and Comparing Results
Statistical Calculations
Comparing Objects
Comparing Before-and-After Conditions
Detecting Changes to Objects
Comparing File Contents
Saving Snapshots for Later Use
Exporting Pipeline Results
Suppressing Results
Changing Pipeline Formatting
Forcing Text Display
Excel: Exporting Objects
HTML Outputs
The Extended Type System (Part One)
Rendering Text as Text and Only Text
Your Wish Has Priority
Table of Contents | About PowerShell Plus 110 Sponsors | Resources | BBS Technologies
Known Objects and Formatting
Unknown Objects
Emergency Mode
"The Case of the Vanished Column"
ETS Enhancement
Planning Enhancement
Summary
8sing the PowerShell Pipeline
Instruction chains are really nothing new. The old console was able to forward (or "pipe") the results
of a command to the next with the "pipe" operator "|". One of the more known usages was to pipe
data to the tool !ore@ which then would present the data screen page by screen page:
(ir | more
In contrast to the traditional concept of text piping, the PowerShell pipeline takes an object-oriented
approach and implements it in real time. Have a look:
(ir | Sort-Object Length | Select-Object Name, Length |
ConvertTo-Html | Out-File report.htm
.\report.htm
It returns an HTML report on the current directory contents sorted by file size. All of this starts with
a .ir command, which then passes its result to Sort-,b>ect. The sorted result then gets limited to
only the properties you want in the report. "on$ert6o-0t!l converts the objects to HTML which is
then written to a file.
*bJect(oriented Pipeline
What you see here is a true object-oriented pipeline so the results from a command remain rich
objects. Only at the end of the pipeline will the results be reduced to text or HTML or whatever you
choose for output. Take a look at Sort-,b>ect. It sorts the directory listing by file size. If the pipeline
had simply fed plain text into Sort-,b>ect, you would have had to tell Sort-,b>ect just where the file
size information was to be found in the raw text. You would also have had to tell Sort-,b>ect to sort
this information numerically and not alphabetically. Not so here. All you need to do is tell Sort-
,b>ect which object property you want to sort. The object nature tells Sort-,b>ect all it needs to
know: where the information you want to sort is found, and whether it is numeric or letters.
You only have to tell Sort-,b>ect which object property to use for sorting because PowerShell sends
results as rich .NET objects through the pipeline. Sort-,b>ectdoes all the rest automatically. Simply
replace Lenth with another object property, such as 5a!e or LastWrite6i!e, to sort according to
these criteria. Unlike text, information in an object is clearly structured: this is a crucial PowerShell
pipeline advantage.
Table of Contents | About PowerShell Plus 111 Sponsors | Resources | BBS Technologies
Text 7ot Converted 8ntil the End
The PowerShell pipeline is always used, even when you provide only a single command. PowerShell
attaches to your input the cmdlet ,ut-.e7ault which converts the resulting objects into text at the
end of the pipeline.
Even a simple .ir command is appended internally and converted into a pipeline command:
(ir | Out-Default
Of course, the real pipeline benefits show only when you start adding more commands. The chaining
of several commands allows you to use commands like Lego building blocks to assemble a complete
solution from single commands. The following command would output only a directory's text files
listing in alphabetical order:
(ir *.txt | Sort-Object
The cmdlets in Table 5.1 have been specially developed for the pipeline and the tasks frequently
performed in it. They will all be demonstrated in the following pages of this chapter.
Just make sure that the commands you use in a pipeline actually
do process information from the pipeline. The following line, while
it is technically OK, is really useless because notepad.e(e cannot
process pipeline results:
(ir | Sort-Object | notepad
If you'd like to open pipeline results in an editor, you should put the results in
a file first and then open the file with the editor:
(ir | Sort-Object | Out-File result.txtB notepad result.txt
Cmdlet>"uncti
on
)escription
"o!pare-,b>ect
Compares two objects or object collections and marks their
differences
"on$ert6o-0t!l Converts objects into HTML code
-(port-"li(!l Saves objects to a file (serialization)
-(port-"s$ Saves objects in a comma-separated values file
Table of Contents | About PowerShell Plus 112 Sponsors | Resources | BBS Technologies
/or-ach-,b>ect Returns each pipeline object one after the other
/or!at-List Outputs results as a list
/or!at-6able Outputs results as a table
/or!at-Wide Outputs results in several columns
'et-<ni:ue Removes duplicates from a list of values
'roup-,b>ect Groups results according to a criterion
4!port-"li(!l
Imports objects from a file and creates objects out of them
(deserialization)
Measure-,b>ect
Calculates the statistical frequency distribution of object
values or texts
!ore Returns text one page at a time
,ut-/ile Writes results to a file
,ut-0ost Outputs results in the console
,ut-0ost
-pain
Returns text one page at a time
,ut-5ull Deletes results
,ut-Printer Sends results to printer
,ut-Strin Converts results into plain text
Select-,b>ect
Filters properties of an object and limits number of results
as requested
Sort-,b>ect Sorts results
6ee-,b>ect Copies the pipeline's contents and saves it to a file or a
Table of Contents | About PowerShell Plus 113 Sponsors | Resources | BBS Technologies
variable
Where-,b>ect Filters results according to a criterion
Table 1$#: Typical pipeline cmdlets and functions
Streaming: ,eal(time Processing or 7otN
When you combine several commands in a pipeline, you'll want to ask when each separate
command will actually be processed: consecutively or at the same time? The pipeline processes the
results in real time, at least when the commands chained together in the pipeline support real-time
processing. That's why there are two pipeline modes:
SeKuential LslowM mode: In sequential mode, pipeline commands are executed one at a
time. So the command's results are passed on to the next one only after the command has
completely performed its task. This mode is slow and hogs memory because results are
returned only after all commands finish their work and the pipeline has to store the entire
results of each command. The sequential mode basically corresponds to the variable concept
that first saves the result of a command to a variable before forwarding it to the next
command.
Streaming 'ode LKuickM: The streaming mode immediately processes each command
result. Every single result is directly passed onto the subsequent command. It rushes through
the entire pipeline and is immediately output. This quick mode saves memory because results
are output while the pipeline commands are still performing their tasks. The pipeline doesn't
have to store all of the command's results, but only one single result at a time.
<4locking< Pipeline Commands
You can sort pipeline results through a blocking operation because sorting can only take place when
all results are available. This also means there can be long processing times and it can even cause
instability if you don't pay attention to memory requirements:
# Attention: danger!
(ir C:\ -recurse | Sort-Object
If you execute this extreme example, you won't see any signs of
life from PowerShell for a long time. If you let the command run
too long, you may even lose control of your computer and have to
reboot it because it runs out of memory. What's going on here?
In this example .ir returns all files and directors of C:\. These results are
passed by the pipeline to Sort-,b>ect, and because Sort-,b>ectcan only sort
the results when all of them are available, it collects the results as they come
in. Those results then create a "data jam" in the pipeline. The two problem
Table of Contents | About PowerShell Plus 114 Sponsors | Resources | BBS Technologies
areas in sequential mode are:
First problem: You won't see any activity as long as data is being collected.
The more data that has to be acquired, the longer the wait time will be. In
the above example, it can take several minutes.
Second problem: Because enormous amounts of data have to be stored
temporarily before Sort-,b>ect can process them, the memory space
requirement is very high. In this case, it's even higher that the entire
Windows system will respond more and more clumsily until finally you won't
be able to control it any longer.
That's not all. In this specific case, confusing error messages will pile up: if
you have .ir output a complete recurse folder listing, it may encounter
subdirectories where you have no access rights. This will lead to (benign)
error messages that will always be immediately output. Since the results of
the .ir command are passed along the pipeline to the following command,
which collects it before outputting it, error messages will appear out of the
blue.
So, if you use sequential pipeline commands like Sort-Object, which block the
pipeline and wait for all results, make sure the pipeline is not processing
excessive amount of data.
Whether a command supports streaming is up to the programmer. For Sort-,b>ect, there are
technical reasons why this command must first wait for all results. Otherwise, it wouldn't be able to
sort the results. If you use commands that are not designed for PowerShell then clearly their original
programmers could not have taken into account the special demands of PowerShell. For example, if
you use the traditional command !ore.co! to output information one page at a time, it will work
but !ore.co! is also a blocking command that could interrupt pipeline streaming:
# If the preceding command can execute its task quickly,
# you may not notice that it can be a block:
(ir | more.com
# If the preceding command requires much time,
# its blocking action may cause issues:
(ir c:\ -recurse | more.com
But also genuine PowerShell cmdlets, functions, or scripts can block pipelines if the programmer
doesn't use streaming. Surprisingly, PowerShell developers forgot to add streaming support to the
integrated !ore function. This is why !ore essentially doesn't behave much differently than the
ancient !ore.co! command:
# The more function doesn't support streaming, either,
# and that means you'll have to wait:
(ir c:\ -recurse | more
Table of Contents | About PowerShell Plus 115 Sponsors | Resources | BBS Technologies
The cmdlet ,ut-0ost means you don't have to wait. Its parameter -pain also supports page-by-
page outputs. Because this cmdlet supports streaming, you won't have to sit in front of the console
twiddling your thumbs:
(ir c:\ -recurse | Out-Host -paging
In Chapters 9 and 10, you'll learn what a programmer has to watch
out for so that PowerShell cmdlets, functions, or scripts will support
the pipeline streaming mode.
Converting *bJects into Text
At the end of a day, you want commands to return visible results, not objects. So, while results stay
rich data objects while traveling the pipeline, at the end of the pipeline, they must be converted into
text. This is done by (internally) adding ,ut-.e7ault to your input. The following commands are
identical:
(ir
(ir | Out-Default
,ut-.e7ault transforms the pipeline result into visible text. To do so, it first calls /or!at-6able (or
/or!at-List when there are more than five properties to output) internally, followed by ,ut-0ost.
,ut-0ost outputs the text in the console. So, this is what happens internally:
(ir | Format-Table | Out-Host
'aking *bJect Properties Iisible
To really see all the object properties and not just the ones PowerShell "thinks" are important, use
/or!at-6able and add a "*" to select all object properties.
(ir | Format-Table *
PSPat PSPar PSChi PSDri PSPro PSIsC Mode Name Pare Exis Root Full
h entPa ldNam ve vider ontai nt ts Name
th e ner


----- ----- ----- ----- ----- ----- ---- ---- ---- ---- ---- ----
Mi... Mi... Ap... C Mi... True d... A... T... True C:\ C...
Mi... Mi... Ba... C Mi... True d... B... T... True C:\ C...
Mi... Mi... Co... C Mi... True d... C... T... True C:\ C...
Mi... Mi... Debug C Mi... True d... D... T... True C:\ C...
Mi... Mi... De... C Mi... True d... D... T... True C:\ C...
Table of Contents | About PowerShell Plus 116 Sponsors | Resources | BBS Technologies
You now get so much information that columns shrink to an unreadable format.
For example, if you'd prefer not to reduce visual display because of
lacking space, use the -wrap parameter, like this:
(ir | Format-Table * -wrap
Still, the horizontal table design is unsuitable for more than just a handful of properties. This is why
PowerShell uses /or!at-List instead of /or!at-6able whenever there are more than five properties
to display, and you should do the same:
(ir | Format-List *
You will now see a list of several lines for each object's property. For a folder, it might look like this:
PSPath : Microsoft.PowerShell.Core\FileSystem::C:\
Users\Tobias Weltner\Music
PSParentPath : Microsoft.PowerShell.Core\FileSystem::C:\
Users\Tobias Weltner
PSChildName : Music
PSDrive : C
PSProvider : Microsoft.PowerShell.Core\FileSystem
PSIsContainer : True
Mode : d-r--
Name : Music
Parent : Tobias Weltner
Exists : True
Root : C:\
FullName : C:\Users\Tobias Weltner\Music
Extension :
CreationTime : 13.04.2007 01:54:53
CreationTimeUtc : 12.04.2007 23:54:53
LastAccessTime : 10.05.2007 21:37:26
LastAccessTimeUtc : 10.05.2007 19:37:26
LastWriteTime : 10.05.2007 21:37:26
LastWriteTimeUtc : 10.05.2007 19:37:26
Attributes : ReadOnly, Directory
A file has slightly different properties:
PSPath : Microsoft.PowerShell.Core\FileSystem::C:\
Users\Tobias Weltner\views.PS1
PSParentPath : Microsoft.PowerShell.Core\FileSystem::C:\
Users\Tobias Weltner
PSChildName : views.PS1
PSDrive : C
PSProvider : Microsoft.PowerShell.Core\FileSystem
PSIsContainer : False
Mode : -a---
Table of Contents | About PowerShell Plus 117 Sponsors | Resources | BBS Technologies
Name : views.PS1
6ength : 282>
=irectory+ame : ':($sers(Tobias )eltner
=irectory : ':($sers(Tobias )eltner
.s<ead?nly : 1alse
Exists : True
FullName : C:\Users\Tobias Weltner\views.PS1
Extension : .PS1
CreationTime : 18.09.2007 16:30:13
CreationTimeUtc : 18.09.2007 14:30:13
LastAccessTime : 18.09.2007 16:30:13
LastAccessTimeUtc : 18.09.2007 14:30:13
LastWriteTime : 18.09.2007 16:46:12
LastWriteTimeUtc : 18.09.2007 14:46:12
Attributes : Archive
The property names are located on the left and their content on the right. You now know how to find
out which properties an object contains.
"ormatting Pipeline ,esults
Transforming objects produced by the pipeline is carried out by formatting cmdlets. There are four
choices:
Get-Command -verb 2ormat
CommandType Name Definition
----------- ---- ----------
Cmdlet Format-Custom Format-Custom [[-Property] <Objec...
Cmdlet Format-List Format-List [[-Property] <Object[...
Cmdlet Format-Table Format-Table [[-Property] <Object...
Cmdlet Format-Wide Format-Wide [[-Property] <Object>...
These formatting cmdlets are not just useful for converting all of an object's properties into text but
you can also select the properties you want to see.
)ispla!ing Particular Properties
To do so, you should type the property that you want to see and not just an asterisk behind the
cmdlet. The next instruction gets you a directory listing with only 5a!e and Lenth. Because
subdirectories don't have a property called Lenth, the Lenth column for the subdirectory is empty:
(ir | Format-Table Name, Length
Name Length
---- ------
Sources
Test
172.16.50.16150.dat 16
Table of Contents | About PowerShell Plus 118 Sponsors | Resources | BBS Technologies
172.16.50.17100.dat 16
output.htm 10834
output.txt 1338
8sing 3ildcard Characters
Wildcard characters are allowed so the next command outputs all running processes that begin with
"l". All properties starting with "pe" and ending in "64" are output:
Get-Process i* | Format-Table name,pe*6#
Name PeakPagedMemory PeakWorkingSet64 PeakVirtualMemory
Size64 Size64
---- --------------- ---------------- -----------------
IAAnotif 3432448 6496256 81596416
IAANTmon 761856 2363392 25346048
Idle 0 0 0
ieuser 12193792 25616384 180887552
iexplore 37224448 52764672 203845632
IfxPsdSv 1396736 3436544 43646976
IFXSPMGT 3670016 9932800 73412608
IFXTCS 3375104 7675904 72654848
iPodService 3231744 5177344 57401344
iTunesHelper 2408448 5935104 70582272
If you want to use even more complex wildcards, regular expressions are permitted (more
information coming in Chapter 13). For example, WMI objects that are returned by 'et-W!i,b>ect
contain a number of properties that PowerShell returns and that all begin with the "__" character. To
exclude these properties, you should use a wildcard like this one:
Get-WmiObject 7in'2E;hare | Format-List Fa-zG*
Status : OK
Type : 2147483648
Name : ADMIN$
AccessMask :
AllowMaximum : True
Caption : Remote Admin
Description : Remote Admin
InstallDate :
MaximumAllowed :
Path : C:\Windows
Status : OK
Type : 2147483648
Name : C$
AccessMask :
AllowMaximum : True
Caption : Default share
Description : Default share
InstallDate :
MaximumAllowed :
Table of Contents | About PowerShell Plus 119 Sponsors | Resources | BBS Technologies
Path : C:\
(...)
Scriptblocks and <S!nthetic< Properties
Scriptblocks can be used as columns as they basically act as PowerShell instructions included in
braces that work like synthetic properties to calculate their value. Within a scriptblock, the variable
$_ contains the actual object. The scriptblock could convert the Lenth property into kilobytes if
you'd like to output file sizes in kilobytes rather than bytes:
(ir | Format-Table Name, 9 FintG$$_.Length/!K)& :
Name [int]($_.Length/1KB)
---- --------------------
output.htm 11
output.txt 13
backup.pfx 2
cmdlet.txt 23
Or perhaps you'd like your directory listing to denote how many days have passed since a file or a
subdirectory was last modified. While the file object doesn't furnish such information, you could
calculate this by means of available properties and provide it its own new property. In the
LastWrite6i!e property, the date of the last modification is indicated. By using the 5ew-6i!eSpan
cmdlet, you can calculate how much time has elapsed up to the current date. To see how this works,
look at the line below as an example that calculates the time difference between January 1, 2000,
and the current date:
New-TimeSpan "01/01/2000" $Get-Date&
Days : 2818
Hours : 11
Minutes : 59
Seconds : 3
Milliseconds : 699
Ticks : 2435183436996134
TotalDays : 2818,49934837516
TotalHours : 67643,9843610037
TotalMinutes : 4058639,06166022
TotalSeconds : 243518343,699613
TotalMilliseconds : 243518343699,613
Use this scriptblock to output how many days have elapsed from the LastWrite6i!e property up to
the current date and to read it out in its own column:
9$New-TimeSpan $_.LastWriteTime $Get-Date&&.Days:
.ir would then return a subdirectory listing that shows how old the file is in days:
(ir | Format-Table Name, Length, `
9$New-TimeSpan $_.LastWriteTime $Get-Date&&.Days: -autosize
Table of Contents | About PowerShell Plus 120 Sponsors | Resources | BBS Technologies
Name Length (New-TimeSpan
$_.LastWriteTime
(Get-Date)).Days
---- ------ ----------------
Application Data 61
Backup 55
Contacts 158
Debug 82
Desktop 19
Documents 1
(...)
Changing Column +eadings
As you use synthetic properties, you'll notice that column headings look strange because PowerShell
puts code in them that computes the column contents. However, after reading the last chapter, you
know that you can use a hash table to format columns more effectively and that you can also
rename them:
$column = ?90-pre66ion=9 FintG$$_.Length/!K)& :B La,el="KB" :
(ir | Format-Table Name, $column
Name KB
---- --
output.htm 11
output.txt 13
backup.pfx 2
cmdlet.txt 23
*ptimi.ing Column 3idth
Text output conforms to the width of your PowerShell console's display buffer as it tries to
accommodate as much data as possible. Because the pipeline processes results in real time, /or!at-
6able cannot know how wide of a space the column elements will occupy. As a result, the cmdlet
tends to be generous in sizing columns. If you specify the -auto parameter, /or!at-6able will collect
all results first before setting the maximum width for all elements. You can optimize output, but the
results will no longer be output in real time:
$column = ?90-pre66ion=9 FintG$$_.Length/!K)& :B La,el="KB" :
(ir | Format-Table Name, $column -auto
Name KB
---- --
output.htm 11
output.txt 13
backup.pfx 2
cmdlet.txt 23
Table of Contents | About PowerShell Plus 121 Sponsors | Resources | BBS Technologies
PropertySets and Iiews
If you don't specify any particular properties behind the formatting cmdlet, PowerShell will
determine which object properties to convert into text. This automatic feature comes from what is
known as the Extended Type System (ETS), which you'll learn more about a bit later. For many
commands, PowerShell supplies PropertySets@which are compilations of especially important object
properties. They make it unnecessary to specify properties manually, yet still receive basic
information.
If you output the result of 'et-Process without further specifications, PowerShell will routinely
convert the following Process properties objects into text:
Get-Process
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
36 2 712 48 21 2616 agrsmsvc
328 9 16620 3752 114 464 AppSvc32
105 3 1044 592 37 1228 Ati2evxx
You can set quite a different priority by specifying a PropertySet like PSResources after /or!at-
6able:
Get-Process | Format-Table ;@e6o8rce6
Name Id Handle Working PagedMem Private VirtualMe Total
Count Set orySize Memory morySize Process
Size or Time
---- -- ------ ------- -------- -------- --------- -------
agrsmsvc 2616 36 49152 729088 729088 21884928
AppSvc32 464 328 3842048 17018880 17018880 119091200
Ati2evxx 1228 105 606208 1069056 1069056 38473728
Ati2evxx 1732 130 3743744 2097152 2097152 50249728
ATSwpNav 2064 79 1069056 4808704 4808704 60739584 00:09
(...)
And PowerShell will select other properties when you use the PropertySet PS"on7iuration:
Get-Process | Format-Table ;Con2ig8ration
Name Id PriorityClass FileVersion
---- -- ------------- -----------
agrsmsvc 2616
AppSvc32 464
Ati2evxx 1228
Ati2evxx 1732
ATSwpNav 2064 Normal 7, 7, 0, 25
This raises the question of what exactly is a PropertySet and how to find out what they are about.
PropertySets are defined for each cmdlet. If you want to see which PropertySets are available for the
'et-Process cmdlet, use Get-Me!ber to list all members of the PropertySet type:
Table of Contents | About PowerShell Plus 122 Sponsors | Resources | BBS Technologies
Get-Process | Get-Member -MemberType ropert<;et

TypeName: System.Diagnostics.Process
Name MemberType Definition
---- ---------- ----------
PSConfiguration PropertySet PSConfiguration {Name, Id,
PriorityClass, FileVersion}
PSResources PropertySet PSResources {Name, Id, Handlecount,
WorkingSet, NonPagedMemorySize,
PagedMemory...
The properties that make a PropertySetvisible are listed after the respective PropertySet. As you
see, two PropertySets exist for the 'et-Process cmdlet. No practical PropertySets are defined for
most other cmdlets, but you make up for that. In the section about the ETS toward the end of this
chapter, you'll learn how to define a command's properties that are most important for you as a
PropertySet.
Views work in a similar way as they set not just the properties that are to be converted into text, but
they can also specify column names or widths and even group information.
# All running processes grouped after start time:
Get-Process | Format-Table -view ;tart3ime
# All running processes grouped according to priority:
Get-Process | Format-Table -view riorit<
Views are highly specific and always apply to particular object types and particular formatting
cmdlets. The Priority view applies only to /or!at-6able and only when you display Process objects
with it. This view doesn't work for /or!at-List:
Get-Process | Format-List -view riorit<
1ormat-6ist : Vie% name /riority cannot be found.
At line:1 char:*9
,et-/rocess M 1ormat-6ist !!!! -#ie% /riority
You'll get an error message if you try to use it to format a file listing and not processes,:
(ir | Format-Table -view riorit<
1ormat-Table : Vie% name /riority cannot be found.
At line:1 char:1C
=ir M 1ormat-Table !!!! -#ie% /riority
Unfortunately, there is no built-in option for finding out which views are available. In the section on
the ETS, you'll learn solutions to this problem, and you'll also read about how to define your own
views.
Table of Contents | About PowerShell Plus 123 Sponsors | Resources | BBS Technologies
Sorting and Brouping Pipeline ,esults
Your first task is to process and concentrate this information since PowerShell commands often
return large amounts of data. Using the cmdlets Sort-,b>ect and 'roup-,b>ect, you can sort and
group other command results. In the simplest scenario, just append Sort-,b>ect to a pipeline
command and your output will already be sorted. It's really very simple:
(ir | Sort-Object
When you do that, Sort-,b>ect selects the property it uses for sorting. It's better to choose the
sorting criterion yourself as every object property may be used as a sorting criterion. For example,
you could use one to create a descending list of a subdirectory's largest files:
(ir | Sort-Object -property Length -descending
So that you can make good use of Sort-,b>ect and all the other
following cmdlets, you must also know which properties are
available for the objects traveling through the pipeline. In the last
section, you learned how to do that. Send the result of .ir to
/or!at-List + first, then you'll see all properties and you can select
one to use for subsequent sorting:
(ir | Format-List *
The parameter -property allows you to use any object property as a sorting criterion. In this case,
Lenth is used and Sort-,b>ect does the rest of the work itself. You need only describe where the file
size is located (it is clearly available in the Lenth object property). You do not have to state
explicitly that the file size is numeric and so has to be sorted numerically, not alphabetically. Sort-
,b>ect can sort by more than one property at the same time. For example, if you'd like to
alphabetize all the files in a subdirectory by type first (-(tension property) and then by name (5a!e
property), specify both properties:
(ir | Sort-Object 0-ten6ion, Name
Sort *bJect and +ash Tables
Sort-,b>ect not only uses properties for sorting operations. You may also use hash tables as an
alternative. Let's assume that you want to sort a subdirectory listing by file size and name, while the
file size must be sorted in descending and names in ascending order. How do you accomplish that?
In any case, not like this:
(ir | Sort-Object Length, Name -descending, -ascending
5ort-?bNect : A parameter could not be found that matches
parameter name "5ystem.?bNectOP".
At line:1 char:18
Table of Contents | About PowerShell Plus 124 Sponsors | Resources | BBS Technologies
=ir M 5ort-?bNect !!!! 6ength, +ame -descending, -ascending
You can solve this problem by passing Sort-,b>ect to a hash table (see Chapter 4).
(ir | Sort-Object ?9e-pre66ion="Length"B(e6cending=$true:, `
?9e-pre66ion="Name"B.6cending=$true:
The hash table makes it possible to append additional information to a property, so you can
separately specify for each property the sorting sequence you prefer.
Apropos hash tables: can you sort these, too? At first glance, it would seem so:
$hash=?9"Tobias"=A"B"Martina"=A"B"Cofi"=I"B"Zumsel"=!"":
$hash | Sort-Object Dal8e -descending
Name Value
---- -----
Tobias 90
McGuffin 100
Cofi 80
Martina 90
Yet it does work if you pass the enumerator directly to Sort-,b>ect. This is what you'll get with
'et-nu!erator%&:
$hash.GetEnumerator$& | Sort-Object Dal8e -descending
Name Value
---- -----
Zumsel 100
Martina 90
Tobias 90
Cofi 80
Brouping &n0ormation
'roup-,b>ect works by grouping similar objects and then reporting their number. You only need
specify the property to 'roup-,b>ect as your grouping criterion. The next line returns a good status
overview of services:
Get-Service | Group-Object ;tat86
Count Name Group
----- ---- -----
91 Running {AeLookupSvc, AgereModemAudio, Appinfo, Ati
External Event Utility...}
67 Stopped {ALG, AppMgmt, Automatic LiveUpdate - Scheduler,
BthServ...}
Table of Contents | About PowerShell Plus 125 Sponsors | Resources | BBS Technologies
In this case, 'roup-,b>ect returns an object for every group. The number of groups depends only on
how many different values could be found in the property specified in the grouping operation. The
Status property always returns either the values "running" or "stopped" for services. This is why
'roup-,b>ect returned exactly two objects in this example.
The results' object always contains the properties "ount, 5a!e, and 'roup. Services are grouped
according to the desired criteria in the 'roup property. The following shows how you could obtain a
list of all currently running services:
$result = Get-Service | Group-Object ;tat86
$resultF"G.Group
It works in a very similar way for other objects. In a file system, 'roup-,b>ect would put file types
in a subdirectory and list their frequency if you use -(tension as grouping property:
(ir | Group-Object 0-ten6ion
Of course, you could subsequently also sort the result:
(ir | Group-Object 0-ten6ion | Sort-Object Co8nt -descending
Count Name Group
----- ---- -----
22 {Application Data, Backup, Contacts, Debug...}
16 .ps1 {filter.ps1, findview.PS1, findview2.PS1, findvi...}
12 .txt {output.txt, cmdlet.txt, ergebnis.txt, error.txt...}
4 .csv {ergebnis.csv, history.csv, test.csv, test1.csv}
3 .bat {ping.bat, safetycopy.bat, test.bat}
2 .xml {export.xml, now.xml}
2 .htm {output.htm, report.htm}
8sing Brouping Expressions
'roup-,b>ect not only groups by set properties but also can use PowerShell expressions. These must
be specified in braces behind 'roup-,b>ect. The respective object is within the expression as is
customary for a =3 variable. The expression can report back on any results. Then 'roup-,b>ect
groups the objects accordingly.
In the following line, the expression returns 6rue if the file size exceeds 100 KB or /alse as the line
returns two groups, 6rue and /alse. All files larger than 100KB are in the 6rue group:
(ir | Group-Object 9$_.Length -gt !""K):
Count Name Group
----- ---- -----
67 False {Application Data, Backup, Contacts, Debug...}
2 True {export.xml, now.xml} in the column Count...
Table of Contents | About PowerShell Plus 126 Sponsors | Resources | BBS Technologies
However, the expression's return value doesn't have to be either 6rue or /alse, but is arbitrary. In
the next line, the expression determines the file name's first letter and returns this in capitals. The
result: 'roup-,b>ect groups the subdirectory contents by first letters:
(ir | Group-Object 9$_.name.SubString$",!&.toUpper$&:
Count Name Group
----- ---- -----
4 A {Application Data, alias1, output.htm, output.txt}
2 B {Backup, backup.pfx}
2 C {Contacts, cmdlet.txt}
5 D {Debug, Desktop, Documents, Downloads...}
5 F {Favorites, filter.ps1, findview.PS1, findview2.PS1...}
3 L {Links, layout.lxy, liste.txt}
3 M {MSI, Music, meinskript.ps1}
3 P {Pictures, p1.nrproj, ping.bat}
7 S {Saved Games, Searches, Sources, SyntaxEditor...}
15 T {Test, test.bat, test.csv, test.ps1...}
2 V {Videos, views.PS1}
1 [ {[test]}
1 1 {1}
4 E {result.csv, result.txt, error.txt, export.xml}
4 H {mainscript.ps1, help.txt, help2.txt, history.csv}
1 I {info.txt}
2 N {netto.ps1, now.xml}
3 R {countfunctions.ps1, report.htm, root.cer}
2 U {unsigned.ps1, .ps1}
If you take a closer look at the 'roup-,b>ect result, you'll notice that after each group name is an
array in which single group objects are summarized. So, you could output a practical, alphabetically
grouped directory view from this result:
(ir | Group-Object 9$_.name.SubString$",!&.toUpper$&: |
ForEach-Object 9 $$_.Name&**B "======="B $_.Group:
(...)
BBBBBBB
=======
d---- 26.07.2007 11:03 Backup
-a--- 17.09.2007 16:05 1732 backup.pfx
CCCCCCC
=======
d-r-- 13.04.2007 15:05 Contacts
-a--- 13.08.2007 13:41 23586 cmdlet.txt
DDDDDDD
=======
d---- 28.06.2007 18:33 Debug
d-r-- 30.08.2007 15:56 Desktop
d-r-- 17.09.2007 13:29 Documents
d-r-- 24.09.2007 11:22 Downloads
-a--- 26.04.2007 11:43 1046 drive.vbs
(...)
Table of Contents | About PowerShell Plus 127 Sponsors | Resources | BBS Technologies
Of course, it will cost a little memory space to store the grouped objects in arrays. Use the
parameter -noele!ent if you don't need the grouped objects.. You could then receive a quick listing
of how many processes of which companies are running on your computer. However, because of the
-noele!ent parameter, you will not be able to see any longer which processes these are in detail:
Get-Process | Group-Object -property Compan< -noelement
Count Name
----- ----
50
1 AuthenTec, Inc.
2 LG Electronics Inc.
1 Symantec Corporation
2 ATI Technologies Inc.
30 Microsoft Corporation
1 Adobe Systems, Inc.
1 BIT LEADER
1 LG Electronics
1 Intel Corporation
2 Apple Inc.
1 BlazeVideo Company
1 ShellTools LLC
2 Infineon Technologies AG
1 Just Great Software
1 Realtek Semiconductor
1 Synaptics, Inc.
8sing "ormatting Cmdlets to "orm Broups
'roup-,b>ect isn't the only option for grouping information. Formatting cmdlets like /or!at-6able or
/or!at-List can also group information if you use the -roup9y parameter. You can specify the
property that you want to use as a grouping criterion after it. For example, if you'd like to group a
subdirectory's contents by file type, use the -(tension property:
(ir | Format-Table -groupBy 0-ten6ion
The result appears to be correct at first glance. However, if you look more carefully, you'll find many
groups repeated as /or!at-6able tried not to disrupt the streaming pipeline and processed the files
unleashed by .ir running through the pipeline in real time. This leads to a continual accumulation of
new groups as files pass through the pipeline that no longer fit into the current group. So, if you
want to form groups, you will need to interrupt pipeline streaming and sort the files first, based on
the criterion you want to group them afterwards:
(ir | Sort-Object 0-ten6ion, Name | Format-Table -groupBy 0-ten6ion

Directory: Microsoft.PowerShell.Core\FileSystem::C:\Users\
Tobias Weltner
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 10.08.2007 11:28 116 ping.bat
Table of Contents | About PowerShell Plus 128 Sponsors | Resources | BBS Technologies
-a--- 18.09.2006 23:43 24 backupcopy.bat
-a--- 15.08.2007 20:00 569 test.bat
Directory: Microsoft.PowerShell.Core\FileSystem::C:\Users\
Tobias Weltner
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 15.08.2007 08:44 307 history.csv
-a--- 15.08.2007 09:35 8160 test.csv
In this example, the result of .ir is directly sorted by Sort-,b>ect
according to two properties, first by extension and then by name.
The result is that the groups are sorted alphabetically by name.
"iltering Pipeline ,esults
Pipeline filters allow only certain objects or object properties through the pipeline. That's practical,
because often you will want all results that a command returns. Where-,b>ect permits only those
objects to pass through that meet certain criterion. Select-,b>ect also allows only certain object
properties to travel through the pipeline. You can use /or-ach-,b>ect to process all objects in the
pipeline sequentially, enabling you to make your own filters. Finally, 'et-<ni:ue removes pipeline
duplicates. Let's take a closer look at filters.
"iltering *bJects *ut o0 the Pipeline
If you're only interested in certain objects, assign Where-,b>ect the task of closely examining all
objects and allowing only those through that meet your criterion, which consists of object properties.
For example, if you don't want to view all services returned by 'et-Ser$ice, but only currently
running services, you'll first have to know which service object property reveals whether the service
is running or not. You will need more detailed knowledge about the properties supported by an
object.
You already know how to ferret out these properties. If you'll recall, /or!at-List neatly lists all of an
object's properties when you use the asterisk character as an argument. You will only need an object
example that you can examine with /or!at-List.
To do so, use the same command that you will want to use later in your pipeline, such as 'et-
Ser$ice and save its result to a variable. As commands return their results in arrays and store each
object in it as array elements, you can take the first element you find out of the array and pass it to
/or!at-List:
$result = Get-Service
$resultF"G | Format-List *
Name : AeLookupSvc
CanPauseAndContinue : False
CanShutdown : False
Table of Contents | About PowerShell Plus 129 Sponsors | Resources | BBS Technologies
CanStop : True
DisplayName : Applicationlookup
DependentServices : {}
MachineName : .
ServiceName : AeLookupSvc
ServicesDependedOn : {}
ServiceHandle :
5tatus : <unning
ServiceType : Win32ShareProcess
Site :
Container :
Now, you can already see all of the object's properties and then its current values. It should be
obvious right away that the information sought can be found in the Status property, so you only
want to view the objects whose Status property contains the "running" value. You're now ready to
use the pipeline filter:
Get-Service | Where-Object 9 $_.Status -eq "Running" :
Status Name DisplayName
------ ---- -----------
Running AeLookupSvc Applicationlookup
Running AgereModemAudio Agere Modem Call Progress Audio
Running Appinfo Applicationinformation
Running AppMgmt Applicationmanagement
Running Ati External Ev... Ati External Event Utility
Running AudioEndpointBu... Windows-Audio-Endpoint-building
Running Audiosrv Windows-Audio
Running BFE Basis Filter Engine
Running BITS Intelligent Background Transmiss...
(...)
In fact, it works just the way you want it to work so that now you can see only those services that
are actually running. How does Where-,b>ect function? The cmdlet expects you to type a PowerShell
command in braces and evaluate the command for every pipeline object. The object that Where-
,b>ect was just examining can always be found in the variable =3. =3.Status returns the Status
property content and needs only be compared to the value that you want to let through.
In reality, the instruction behind Where-,b>ect works like a condition (see Chapter 7): if the
expression results in =true, the object will be let through. That's why you may formulate conditions
as complex as you like, but you must only make sure that your instruction results in either =true or
=7alse.
The pipeline filter's principle may be applied to all object types and works in the same way
everywhere. As an experienced administrator, you may be a little disappointed that the service
objects returned by 'et-Ser$ice contain relatively little information. If you want to list all services
that would automatically start, but at the moment aren't running, you can leverage the built-in
Windows Management Instrumentation (WMI) infrastructure as an information source to supply
more data. You'll harvest much more information when you ask it about services:
$services = Get-WmiObject 7in'2E;er4ice
$servicesF"G | Format-List *
Table of Contents | About PowerShell Plus 130 Sponsors | Resources | BBS Technologies
Name : AeLookupSvc
Status : OK
ExitCode : 0
DesktopInteract : False
ErrorControl : Normal
PathName : C:\Windows\system32\svchost.exe -k netsvcs
ServiceType : Share Process
StartMode : Auto
__GENUS : 2
__CLASS : Win32_Service
__SUPERCLASS : Win32_BaseService
__DYNASTY : CIM_ManagedSystemElement
__RELPATH : Win32_Service.Name="AeLookupSvc"
__PROPERTY_COUNT : 25
__DERIVATION : {Win32_BaseService, CIM_Service, CIM_Logic
alElement, CIM_ManagedSystemElement}
__SERVER : TOBIASWELTNE-PC
__NAMESPACE : root\cimv2
__PATH : \\TOBIASWELTNE-PC\root\cimv2:Win32_Service.
Name="AeLookupSvc"
AcceptPause : False
AcceptStop : True
Caption : Applicationlookup
CheckPoint : 0
CreationClassName : Win32_Service
Description : Processes application compatibility cache
requirements when applications start.
DisplayName : Applicationlookup
InstallDate :
ProcessId : 1276
ServiceSpecificExitCode : 0
Started : True
StartName : localSystem
State : Running
SystemCreationClassName : Win32_ComputerSystem
SystemName : TOBIASWELTNE-PC
TagId : 0
WaitHint : 0
The information needed for your criteria are located in the Started and StartMode properties. And
because the Where-,b>ect pipeline filter is used very often, there exists a practical abbreviation for
it: "?". Here is an example of what your pipeline filter could look like:
Get-WmiObject 7in'2E;er4ice |
L 9$$_.Started -eq $false& -and $$_.StartMode -eq "Auto"&: |
Format-Table
ExitCode Name ProcessId StartMode State Status
-------- ---- --------- --------- ----- ------
0 Automatic... 0 Auto Stopped OK
0 ehstart 0 Auto Stopped OK
0 LiveUpdate... 0 Auto Stopped OK
Table of Contents | About PowerShell Plus 131 Sponsors | Resources | BBS Technologies
0 WinDefend 0 Auto Stopped OK
If everything works properly, these lines shouldn't report any services at all because services in the
"auto" start-up mode are automatically started and, for this reason, should be running. If you're
notified of services, you should verify whether these services are (no longer) running despite auto
start. One cause could be that the service has completed its task and was then ended as scheduled.
Incidentally, because WMI objects are not on the internal PowerShell list, results are always
displayed as lists. For this reason, at the end our above example, we set a table format using
/or!at-6able, which is much clearer.
The internal WMI service will provide you with helpful information
about your computer in response to almost any question. You'll
find out exactly what it is in Chapter 18. If you use the -:uery
parameter, you can pass SQL-type queries to this service so that
the command will automatically return only the information sought
and make pipeline filtering superfluous. You should always keep in mind
when using any command that the pipeline filter is practical and easy to use,
but not particularly economical. It limits results that are already available. It
is better right at the beginning to ask only about the information needed, as
it is not as easy to do for all commands as it is for 'et-W!i,b>ect:
Get-WmiObject -query "select * from win32_Service where `
Started=false and StartMode='Auto'" | Format-Table

ExitCode Name ProcessId StartMode State Status
-------- ---- --------- --------- ----- ------
0 Automatic Li... 0 Auto Stopped OK
0 ehstart 0 Auto Stopped OK
0 LiveUpdate N... 0 Auto Stopped OK
0 WinDefend 0 Auto Stopped OK
Selecting *bJect Properties
The information contained in individual objects may be limited as well. You've just seen that some
objects, depending on type, may contain many properties of which you often need only a few. By
using Select-,b>ect, you can select those properties that really interest you. All other properties will
not be allowed through by Select-,b>ect. For example, the following lines will acquire the user object
for the integrated Guest account of your computer:
Get-WmiObject 7in'2EJ6er.cco8nt -filter `
"LocalAccount=True AND Name='guest'"
AccountType : 512
Caption : TobiasWeltne-PC\guest
Domain : TobiasWeltne-PC
Table of Contents | About PowerShell Plus 132 Sponsors | Resources | BBS Technologies
SID : S-1-5-21-3347592486-2700198336-2512522042-501
FullName :
Name : guest
Most of these properties won't interest you, so Select-,b>ect was able to remove them. In the
following, just three of your specified properties were returned:
Get-WmiObject 7in'2EJ6er.cco8nt -filter `
"LocalAccount=True AND Name='guest'" |
Select-Object Name, (i6a,led, (e6cription
Name Disabled Description
---- -------- -----------
guest True Default account for guests...
You could have had the same result if you had used a formatting cmdlet. That would even have been
to your advantage since you could use the -autosi#e parameter to optimize column width:
Get-WmiObject 7in'2EJ6er.cco8nt -filter `
"LocalAccount=True AND Name='guest'" |
Format-Table Name, (i6a,led, (e6cription -autosize
Name Disabled Description
---- -------- -----------
guest True Default account for guest access to computer or domain
The significant difference: /or!at-6able converts properties specified to the object into text. In
contrast, Select-,b>ect creates a completely new object containing just these specified properties:
Get-WmiObject 7in'2EJ6er.cco8nt -filter `
"LocalAccount=True AND Name='guest'" |
Select-Object Name, (i6a,led, (e6cription |
Format-Table *
Name Disabled Description
---- -------- -----------
guest True Default account for guest access to computer or domain
You should make sparing use of Select-,b>ect because it takes a
disproportionate effort to create a new object. Instead, use
formatting cmdlets to specify which object properties are to be
displayed. Select-,b>ect is particularly useful when you don't want
to convert a pipeline result into text, but instead want to output a
comma-separated list using -(port-"s$ or HTML code using "on$ert6o-0t!l.
If you type an asterisk as wildcard character after Select-,b>ect, all properties will be marked as
relevant. Formatting cmdlets will now output all object properties:
Table of Contents | About PowerShell Plus 133 Sponsors | Resources | BBS Technologies
(ir | Select-Object * | Format-Table -wrap
If you'd like to view nearly all of an object's properties, it's easier to display only the properties you
don't want by typing the parameter -e(clude to specify those properties you want to remove from
the object. The next line will output all of a file's properties and directory objects, except for those
beginning with "PS" (and show internal PowerShell help properties):
(ir | Select-Object * -exclude ;*
Limiting 7umber o0 *bJects
Select-,b>ect filters not only object properties but can also, if you prefer, reduce the number of
objects allowed to traverse the pipeline. This function is considerably more interesting because it
allows you to view, among others, the five largest files of a directory or the five processes that have
been running the longest:
# List the five largest files in a directory:
(ir | Sort-Object Length -descending |
Select-Object -first %
# List the five longest-running processes:
Get-Process | Sort-Object ;tart3ime |
Select-Object -last % | Format-Table roce66Name, ;tart3ime
# Alias shortcuts make the line shorter but also harder to read:
gp6 | 6ort ;tart3ime -ea ;ilentl<Contin8e |
6elect -last % | 2t roce66Name, ;tart3ime
ProcessName StartTime
----------- ---------
iexplore 20.09.2007 15:00:20
iexplore 20.09.2007 15:05:26
iexplore 20.09.2007 15:30:51
PowerShellPlus.vshost 20.09.2007 16:07:54
iexplore 20.09.2007 16:56:20
A couple of things are interesting here. For example, if you sort a list of processes by Start6i!e,
you'll presumably get several error messages. If you aren't logged on with administrator privileges,
you may not retrieve the information from some processes. However, you can avoid this difficulty by
setting Sort-,b>ect with parameter --rrorAction (in short: -ea) to Silently"ontinue. This option is
available for nearly every cmdlet and makes sure that error messages won't be displayed.
As a result of such restricted access, not all processes will have any control at all over Start6i!e.
Wherever you can't read the start time because you don't have administrator privileges, a null value
will be returned, which messes up the sorting result. You wouldn't get the right results if you wanted
to use -7irst to view the processes that last started running,:
Get-Process | Sort-Object ;tart3ime |
Select-Object -first % |
Format-Table roce66Name, ;tart3ime
ProcessName StartTime
----------- ---------
Table of Contents | About PowerShell Plus 134 Sponsors | Resources | BBS Technologies
services
SLsvc
SearchIndexer
opvapp
sdclt
Sort-,b>ect uses the value 0 for empty properties. That's why PowerShell gives you the processes
for which it couldn't find any start times. This is interesting since those would be exactly the
processes which you have no full access rights. However, this is a problem you can solve, and you
already know how: by using the pipeline as just previously described. You should simply filter all of
the pipeline's objects out that have an empty Start6i!e property so that you can better understand
what those processes actually are, and then add the .escription property in the output. That's where
process objects record a brief description of the process:
Get-Process | Where-Object 9$_.StartTime -ne $null: |
Sort-Object ;tart3ime | Select-Object -first % |
Format-Table roce66Name, ;tart3ime, (e6cription
ProcessName StartTime Description
----------- --------- -----------
taskeng 19.09.2007 09:35:19 Task planning module
dwm 19.09.2007 09:35:19 Desktop window manager
explorer 19.09.2007 09:35:19 Windows Explorer
GiljabiStart 19.09.2007 09:35:21 Giljabi Start
ATSwpNav 19.09.2007 09:35:21 ATSwpNav Application
If you concatenate several commands in the pipeline, you can use
6ee-,b>ect to skim off intermediate results: either because you
need this information somewhere else, too, or because you want to
check how the pipeline is working.
Get-Process | Tee-Object -variable a! |
Select-Object Name, (e6cription |
Tee-Object -variable a2 |
Sort-Object Name
'et-Process first returns all running processes in this pipeline. Select-,b>ect
removes every object property except for 5a!e and .escription. It then sorts
the processes by name. At two locations in this pipeline, 6ee-,b>ect accesses
the current pipeline result and stores it in a variable without further slowing
or influencing pipeline execution. After the pipeline has done its work, you'll
find the intermediate result in the variables =a1 and =a?, and you'll be able
to analyze it in more depth or use it somewhere else.
If you decide not to set 6ee-,b>ect to the -$ariable parameter, the
intermediate result will be saved to a file, and 6ee-,b>ect will expect you to
provide a file path name. The same applies if you expressly specify the
-7ilePath parameter.
Table of Contents | About PowerShell Plus 135 Sponsors | Resources | BBS Technologies
Processing All Pipeline ,esults Simultaneousl!
If you prefer, you may also submit the results separately to the pipeline and then decide on a case-
by-case basis what to do with them. The right tool is the /or-ach-,b>ect cmdlet that can convert
objects into text:
Get-Service | ForEach-Object 9
"The service {0} is called '{1}': {2}" -f `
$_.Name, $_.DisplayName, $_.Status :
The service AeLookupSvc is called 'Application Lookup': Running
The service AgereModemAudio is called 'Agere Modem Call Progress
Audio': Running
The service ALG is called 'Application Layer Gateway Service': Stopped
The service Appinfo is called 'Application Information': Running
The service AppMgmt is called 'Application Management': Stopped
The service Ati External Event Utility is called 'Ati External Event
Utility': Running
The service AudioEndpointBuilder is called 'Windows-Audio-Endpoint-
Generator': Running
(...)
An instruction block in braces follows /or-ach-,b>ect so you can execute as many PowerShell
commands as you like as long as you separate the commands by ";". This statement block is
executed for every single pipeline object: within the block the current object is available in the $_
variable. In the example, /or-ach-,b>ect output a text for every service retrieved by 'et-Ser$ice
and inserts into the text the three properties 5a!e, .isplay5a!e, and Status.
In case you're asking yourself right now what "-f" is and how to insert
information into text: look it up in Chapter 13, where all the tasks
involving text are explained in detail.
/or-ach-,b>ect is actually just Where-,b>ect's big brother; /or-ach-,b>ect,
Where-,b>ect, can filter out pipeline objects by criterion. To enable /or-ach-
,b>ect to do this, you merely use a condition. That is, only if the condition is met
will the object you want be back in the pipeline. The following lines all lead to the
same result:
Get-Service | Where-Object 9 $_.Status -eq "Running" :
Get-Service | L 9 $_.Status -eq "Running" :
Get-Service | ForEach-Object 9 if $$_.Status -eq "Running"& 9 $_ :
:
Get-Service | % 9 if $$_.Status -eq "Running"& 9 $_ : :
All four lines retrieve a list of currently running services. You see that Where-
,b>ect can be shortened with "?" and /or-ach-,b>ect with "%". You also can see
that Where-,b>ect is actually only /or-ach-,b>ect with a built-in condition. For
Where-,b>ect, the condition is directly within the braces, and for /or-ach-,b>ect
in parentheses after the 47 statement. The rationale for the existence of Where-
Table of Contents | About PowerShell Plus 136 Sponsors | Resources | BBS Technologies
,b>ect is comfort and clarity.
/or-ach-,b>ect actually executes three script blocks, not just one.
If you specify only one script block in braces after /or-ach-,b>ect,
it will be executed once for every pipeline object. If you specify two
script blocks, the first will be executed once and before the first
pipeline object. If you specify three script blocks, the last will be
executed once after the last pipeline object. The following will help you carry
out initialization and tidying tasks or simply output initial and ending
messages:
Get-Service | ForEach-Object 9"Running services:":9
if $$_.Status -eq "Running"& 9 $_ : :9"Done.":
The three script blocks of /or-ach-,b>ect actually correspond to the three
script blocks bein, process, and end, which you'll examine in more detail in
Chapters 9 and 12. You'll understand after reading these chapters that
functions, cmdlets like /or-ach-,b>ect and script blocks, are all three
basically the same.
,emoving )oubles
'et-<ni:ue removes duplicate entries from a sorted list as it presumes that the list was initially
sorted according to criterion to make things easier. 'et-<ni:ue goes through every element on the
list and compares it with the preceding ones. If two are identical, the new object is discarded. So, if
you haven't done any sorting, 'et-<ni:ue won't work:
!,2,',!,2,',!,2,' | Get-Unique
1,2,3,1,2,3,1,2,3
Only after you sort the list-in this case, an array-will doubles be removed:
!,2,',!,2,',!,2,' | Sort-Object | Get-Unique
1,2,3
This method is particularly interesting when you break down text files' contents into single words.
You can use the following line to do so:
$filename = "c:\autoexec.bat"
$$foreach $$line in Get-Content $filename& 9
$line.tolower$&.split$" "&:&
Table of Contents | About PowerShell Plus 137 Sponsors | Resources | BBS Technologies
Then, you could sort this list of each word of a file and then either send it to 'et-<ni:ue (the list of
all words that are in a text) or to 'roup-,b>ect (the number of words used in a text):
$filename = "c:\autoexec.bat"
$$foreach $$line in Get-Content $filename& 9
$line.tolower$&.split$" "&:& | Sort-Object | Get-Unique
$$foreach $$line in Get-Content $filename& 9
$line.tolower$&.split$" "&:& | Sort-Object | Group-Object
Anal!.ing and Comparing ,esults
Using the cmdlets Measure-,b>ect and "o!pare-,b>ect, you can measure and evaluate PowerShell
command results. For example, Measure-,b>ect allows you to determine how often particular object
properties are distributed. "o!pare-,b>ect enables you to compare before-and-after snapshots.
Statistical Calculations
Using the Measure-,b>ect cmdlet, you can carry out statistical calculations so you can work out
minimal, maximal, and average values for a particular object property. For example, if you want to
know how files sizes are distributed in a directory, let .ir give you a directory listing and then
examine the Lenth property:
(ir | Measure-Object Length
Count : 50
Average :
Sum :
Maximum :
Minimum :
Property : Length
Measure-,b>ect counts by default only the specified property's frequency. You should now know that
there are 50 objects that have the Lenth property. Use the relevant parameters if you'd also like to
receive the other statistical statements:
(ir | Measure-Object Length -average -maximum -minimum -sum
Count : 50
Average : 36771,76
Sum : 1838588
Maximum : 794050
Minimum : 0
Property : Length
Measure-,b>ect can also search through other text files and ascertain the frequency of characters,
words, and lines in them:
Get-Content c:\autoexec.bat | Measure-Object -character -line -word
Table of Contents | About PowerShell Plus 138 Sponsors | Resources | BBS Technologies

Lines Words Characters Property
----- ----- ---------- --------
1 5 24
Comparing *bJects
You may often want to compare "before-and-after" conditions to find out which processes have
restarted since a certain point in time, or which services have changed in comparison to a particular
initial state. The "o!pare-,b>ect cmdlet can perform this task by making use of the fact that
PowerShell commands do not retrieve text internally, but real objects.
Comparing 4e0ore(and(A0ter Conditions
For example, you should take a snapshot first if you want to find out whether new processes have
started up, or running processes, have terminated in a certain period of time::
$before = Get-Process
All processes will now be stored in the variable =be7ore. To be exact, =be7ore is an array in which
every process is represented by a process object. You can now compare the current state at any
time you like with this snapshot. Just pass the snapshot list and the list of currently running
processes to "o!pare-,b>ect@which will subsequently establish the differences between the two
lists:
Compare-Object -referenceObject $before `
-differenceObject $Get-Process&
InputObject SideIndicator
----------- -------------
System.Diagnostics.Process (regedit) =>
System.Diagnostics.Process (SearchFilterHost) <=
System.Diagnostics.Process (SearchProtocolHost) <=
If you're wondering right now why the current list of processes
after -di77erence,b>ect is enclosed in parentheses, just remember
that parameters expect actual results. In the example, the list of
currently running processes is acquired as they are newly
generated by the 'et-Process cmdlet. This command must be
placed between parentheses because 'et-Process is a cmdlet and the list in
question... Everything in parentheses will be executed by PowerShell first and
the call result returned afterwards. "o!pare-,b>ect can work with this result.
If you had left out the parentheses, -di77erence,b>ect wouldn't have known
what to do with the 'et-Process specification.
Alternatively, you could, of course, have stored the list of current processes
in a variable first, and then passed this variable, even without parentheses,
Table of Contents | About PowerShell Plus 139 Sponsors | Resources | BBS Technologies
to "o!pare-,b>ect. It's not absolutely necessary to specify the parameter
name if you state the arguments in the right order at the very beginning, that
is, first the list with the "before" state, and then the list with the "after"
state:
$after = Get-Process
Compare-Object $before $after
The Side4ndicator column (line?) reports whether a new process has started running ("=>") or has
been ended in the meantime ("<="). Consequently, "o!pare-,b>ect returns only those processes
that are different. Use -include-:ual as an additional parameter, if you want to see the processes
that have not been changed. Use the additional parameter -e(clude.i77erent, if you'd like to see only
those processes that have not been modified.
)etecting Changes to *bJects
If you use "o!pare-,b>ect as described above, it will only check whether every object in one list is
matched in another list. While comparing them to their initial state, may be sufficient to determine
whether objects were removed or added, you can't use this approach to establish whether an
object's inner status has changed.
For example, if you'd like to verify whether services have stopped or started in comparison to their
defined initial state, "o!pare-,b>ect won't initially help you because when a service is stopped it
still exists: only its inner status has changed. You should instead instruct "o!pare-,b>ect to
compare one or more of the object's properties by using /or!at-List to easily determine which
properties are available to you. You should. first acquire a service object and experiment around
with it a little:
# Pick out Windows Update Service:
$service = Get-Service 58a86er4
# Inspect all properties of this services:
$service | Format-List *
Name : wuauserv
CanPauseAndContinue : False
CanShutdown : True
CanStop : True
DisplayName : Windows Update
DependentServices : {}
MachineName : .
ServiceName : wuauserv
ServicesDependedOn : {rpcss}
ServiceHandle :
5tatus : <unning
ServiceType : Win32ShareProcess
Site :
Container :
Table of Contents | About PowerShell Plus 140 Sponsors | Resources | BBS Technologies
It quickly turns out that the Status property retrieves the desired information. So, you could first
make another snapshot of all services, stop a service subsequently, and then instruct "o!pare-
,b>ect to use the Status property to ascertain differences:
# Save current state:
$before = Get-Service
# Pick out a service and stop this service:
# (Note: this usually requires administrator rights.
# Stop services only if you are sure that the service
# is absolutely not required.
$service = Get-Service 58a86er4
$service.Stop$&
# Record after state:
$after = Get-Service
# A simple comparison will not find differences because
# the service existed before and after:
Compare-Object $before $after
# A comparison of the Status property reports the halted
# service but not its name:
Compare-Object $before $after -Property ;tat86
Status SideIndicator
------ -------------
Stopped =>
Running <=
# A comparison with the Status and Name properties returns
# the required information:
Compare-Object $before $after -Property ;tat86, Name

Status Name SideIndicator
------ ---- -------------
Stopped wuauserv =>
Running wuauserv <=
If you instruct "o!pare-,b>ect with the parameter -property to compare the Status and 5a!e
properties, you'll receive the information you want: the service wuauser$ was executed in the list in
=be7ore, but not in the list in =a7ter. So it was stopped.
This example shows how to stop services. In the next chapter,
you'll learn more about the methods (commands) built into objects.
What's important to note here is only that you change the state of
any service. You could also accomplish that by using the Microsoft
Management Console Snapin for services:
services.msc
Start or stop only those services that you know won't incur any risk when you
start or stop them. If an error message pops up when you try to modify a
service, this is usually because you don't have administrator rights. Just
Table of Contents | About PowerShell Plus 141 Sponsors | Resources | BBS Technologies
remember that for Vista, or when group policies are in effect, that you must
start up PowerShell with administrator rights. Otherwise, you're only a
normal user, even if you log on with an administrator account.
Since the "o!pare-,b>ect results consist of objects, you could make a further analysis of the result.
Perhaps all that interests you are executed modifications. Use a pipeline filter, such as Where-
,b>ect, to specify to the filter that you're interested in only those objects in which the Side4ndicator
property corresponds to the value "=":
Compare-Object $before $after -property ;tat86, Name |
Where-Object 9 $_.SideIndicator -eq "=>" :
Status Name SideIndicator
------ ---- -------------
Stopped wuauserv =>
If you'd like to formulate the result in plain text, use a loop, such as, Where-,b>ect, and use the
information in the retrieved objects to put together the plain text:
Compare-Object $before $after -property ;tat86, Name |
Where-Object 9 $_.SideIndicator -eq "=>" : |
ForEach-Object 9 "The service {0} has changed its status to {1}" `
-f $_.Name, $_.Status:
The service wuauserv has changed its status to Stopped
You can use this same procedure for widely varying monitoring tasks. Think in advance about which
command you could use to determine an object's status to be monitored and, which of the object's
properties will describe its status. For example, if you want to find out whether files in a directory
have changed, the right command would be .ir and the property could be Lenth (because of the
changed file size) or LastWrite6i!e (the contents could have been changed even if its size is just as
large as it was before). Here's an example:
# Create test file and Before snapshot of the directory:
"Hello" > test.txt
$before = (ir
# Modify test file and create After snapshot of the directory:
"Hello world" > test.txt
$after = (ir
# Compare-Object reports all files whose size has changed:
Compare-Object $before $after -property Length, Name
Length Name SideIndicator
------ ---- -------------
26 test.txt =>
16 test.txt <=
# Files whose size is unchanged, however, were not recognized
Table of Contents | About PowerShell Plus 142 Sponsors | Resources | BBS Technologies
# although they were changed:
"Hey!" > test.txt
$after = (ir
Compare-Object $before $after -property Length, Name
# So, when comparing, it is crucial to select a meaningful
# property, e.g., LastWriteTime:
Compare-Object $before $after -property Length, La6t7rite3ime, Name

Length LastWriteTime Name SideIndicator
------ ------------- ---- -------------
16 20.09.2007 14:13:09 test.txt =>
16 20.09.2007 14:13:02 test.txt <=
Comparing "ile Contents
A special form of the "snapshot" is a file's text contents. If you read text contents using 'et-
"ontent, you'll get an array with lines of text. "o!pare-,b>ect can compare this array again and
determine which lines within text files have changed: Here's another example:
# Create first test file:
?"
>> Hello
>> world
>> "? > test1.txt
>>
# Create second test file:
?"
>> Hello
>> beautiful
>> world
>> "? > test2.txt
>>
# Compare both files and show only differing lines:
Compare-Object -referenceObject $$Get-Content test1.txt& `
-differenceObject $$Get-Content test2.txt&
InputObject SideIndicator
----------- -------------
beautiful =>
Compare-Object -referenceObject $$Get-Content test1.txt& `
-differenceObject $$Get-Content test2.txt& -includeEqual
InputObject SideIndicator
----------- -------------
Hello ==
world ==
beautiful =>
Table of Contents | About PowerShell Plus 143 Sponsors | Resources | BBS Technologies
Saving Snapshots 0or Later 8se
Some before-and-after comparisons may not be able to be completed in one day. Perhaps you would
like to compare operating states over a longer time period, and are not sure if the computer (and
your PowerShell) is running the entire time without interruptions. Or maybe you would like to use
the same precisely set initial state. In this case, you can "serialize" the objects in the initial state. In
other words, the objects are stored as a file in a special data format, more or less "frozen." Later,
you can load the object at any time from the file and use them for comparison.
The -(port-"li(!l cmdlet carries out serialization. All you need to do is to specify a file name under
which the objects can be saved. For example, the following line saves a list of all running processes
to the file be7ore.(!l:
Get-Process | Export-Clixml ,e2ore.-ml
Because the initial state is now stored as a file, you could close PowerShell and reboot your
computer. As soon as you are ready to compare the current processes with the stored initial status,
you can load the file back in PowerShell:
$before = Import-Clixml ,e2ore.-ml
However, if you try to compare the contents of =be7ore with the current list of processes, "o!pare-
,b>ect will output an endless list of deviations:
$after = Get-Process
Compare-Object $before $after
In the simplest scenario, "o!pare-,b>ect only verifies whether the objects are in both lists. But as
soon as you serialize or "freeze" objects, your object type changes. If you use 4!port-"li(!l later to
input these objects, the information will be brought back to life in a different type while the objects
will continue to contain all information. Why? Because the re-input objects no longer correspond to
running processes but are the "unfrozen" older processes.
You already know the solution to the problem: simply instruct "o!pare-,b>ect to compare particular
properties because the revived objects continue to contain all the important information. As soon as
you compare objects, "o!pare-,b>ect doesn't care at all about the object type as long as the
objects to be compared support the same properties:
Compare-Object $before $after -property Name
Name SideIndicator
---- -------------
notepad =>
regedit <=
You now know that a process called notepad has been added since the snapshot and a process called
reedit was ended. However, you wouldn't yet know whether the processes that have the same
name are in fact identical. To find out, you would have to include additional object properties in the
comparison, such as the process ID, which clearly identifies processes:
Compare-Object $basis $Get-Process& -property 1d, Name
Table of Contents | About PowerShell Plus 144 Sponsors | Resources | BBS Technologies
Id Name SideIndicator
-- ---- -------------

7788 notepad =>
8004 PowerShellPlus.vshost =>
3032 PowerShellPlus.vshost <=
344 regedit <=
Now, you can see as well that PowerShell was started up again once. The instance of PowerShell
with the process ID L00M was ended and in its place a new instance of PowerShell with the process
ID G0G? was started.
Exporting Pipeline ,esults
You have learned that pipeline results are converted into text when they reach the pipeline's end at
the latest and are output in the console because PowerShell appends the ,ut-.e7ault cmdlet to the
end of every entry. As a result, this cmdlet decided where pipeline results will be output. Along with
,ut-.e7ault, there are a number of additional output cmdlets that you can put at your pipeline's end
so the result is redirected to a file or printed out rather than output in the console. The pipeline
stops its work on reaching the first output cmdlet; if you enter one, ,ut-0ost, which PowerShell
appends automatically, won't go into operation:
Get-Command -verb o8t
CommandType Name Definition
----------- ---- ----------
Cmdlet Out-Default Out-Default [-InputObject <PSObject>]...
Cmdlet Out-File Out-File [-FilePath] <String> [[-Enco...
Cmdlet Out-Host Out-Host [-Paging] [-InputObject <PSO...
Cmdlet Out-Null Out-Null [-InputObject <PSObject>] [-...
Cmdlet Out-Printer Out-Printer [[-Name] <String>] [-Inpu...
Cmdlet Out-String Out-String [-Stream] [-Width <Int32>]...
(ir | Out-File output.txt
.\output.txt
(ir | Out-Printer
,ut-/ile supports the parameter -encodin, which you can use to
determine the format in which information is written to a file. If
you don't remember which encoding formats are allowed, just
specify a value which you know is absolutely false, and then the
error message will tell you which values are allowed:
(ir | Out-File -encoding (8nno
?ut-1ile : 'annot #alidate argument "=unno" because it does
not
belong to the set "unicode, utf&, utf8, utf4*, ascii,
bigendianunicode, default, oem".
Table of Contents | About PowerShell Plus 145 Sponsors | Resources | BBS Technologies
At line:1 char:*>
=ir M ?ut-1ile -encoding !!!! =unno
An alternative to ,ut-/ile is -(port-"s$. You can specify comma-separated
lists with this cmdlet. You'll read more about that a little later on.
Suppressing ,esults
Send the output to ,ut-5ull if you want to suppress command output:
# This command not only creates a new directory but also returns
# the new directory:
md te6tdirector<
Directory: Microsoft.PowerShell.Core\FileSystem::C:\Users\
Tobias Weltner
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 19.09.2007 14:31 testdirectory
rm te6tdirector<
# Here the command output is sent to "nothing"
md te6tdirector< | Out-Null
rm te6tdirector<
# That matches the following redirection:
md te6tdirector< > $null
rm te6tdirector<
Changing Pipeline "ormatting
At first glance, ,ut-0ost seems somewhat superfluous since all results will end up in the console
when you don't specify any output cmdlet. So what's the use of ,ut-0ost? On the one hand, this
cmdlet supports optional parameters like -pain, which can be used to output information page by
page. You already tried that at the beginning of this chapter. In addition, you can use ,ut-0ost to
control pipeline formatting, which in itself is much more important.
The reason is that all output cmdlets not only output all pipeline results to the relevant output
device, but also automatically convert pipeline objects into readable text. You've already learned
how this conversion works by formatting cmdlets like /or!at-6able. This gets interesting when you
specify neither a formatting nor an output cmdlet in your pipeline. Then, PowerShell takes action
automatically, though sometimes the result can be confusing.
For example, can you explain why the next instruction outputs all services in table form, but the
following in list form?
# Outputs services in table form:
Table of Contents | About PowerShell Plus 146 Sponsors | Resources | BBS Technologies
Get-Service
# Outputs services in list form:
Get-LocationB Get-Service
In the second line, the results of two commands are mixed. That's permitted, and you just have to
remember to separate individual commands by a semicolon. None of the two commands outputs its
results by using an output cmdlet. That's why all results remain in the pipeline and are automatically
processed at the end by ,ut-0ost. That's exactly what causes the problem since PowerShell extends
the line in the following way behind the scenes:
& 9Get-LocationB Get-Service: | Out-Default
,ut-.e7ault determines whether you gave one of the formatting cmdlets a particular format. If not,
it tries to find an appropriate format. In doing so, it takes a cue from the first object in the result,
the path name of 'et-Location. However, an unexpectedly colorful series of 'et-Ser$ice services
follows so no predefined format exists with which this muddled medley can be displayed, ,ut-
.e7ault falls back on the list format. You can encounter the problem described here in many places.
It also affects, among others, functions or scripts:
# Example of problem when using a function:
function te6t 9
Get-Location
Get-Service
:
te6t
# Example of problem when using a script:
?"
Get-Location
Get-Service
"? > test.ps1
.\test.ps1
The solution to this problem: either specify a format for the pipeline yourself or send the results of
individual commands to the console:
# Specify the output format yourself so that PowerShell won't need
# to specify the format:
Get-Location | Format-TableB Get-Service
# Or send the intermediate results to the console so that no mixed
# results appear:
Get-Location | Out-HostB Get-Service
"orcing Text )ispla!
PowerShell delays conversion until the last possible moment and converts pipeline objects into text
only until they reach the end of the pipeline since information is typically lost when objects are
converted into text. However, by using ,ut-Strin, you can force PowerShell to convert objects into
text any time you like. ,ut-Strin is the only output cmdlet that continues the pipeline instead of
terminating it. ,ut-Strin puts the objects it receives back into the pipeline as text. You can assign
the result to a variable Because it behaves like a normal pipeline command.:
Table of Contents | About PowerShell Plus 147 Sponsors | Resources | BBS Technologies
$text = (ir | Out-String
$text.toUpper$&
The result of ,ut-Strin is always a single, complete text. That also means that ,ut-Strin blocks
the pipeline stream and waits until all results arrive. If you'd prefer getting the text line by line in an
array, use the -strea! parameter; then ,ut-Strin will transform incoming objects into single blocks
of text in real time and won't block the pipeline:
(ir | Out-String -stream | ForEach-Object 9 $_.toUpper$& :
If possible, you should avoid turning objects into text because that
makes them lose the structure and many options that only original
objects offer.
Excel: Exporting *bJects
All output cmdlets convert pipeline results into text that may be displayed haphazardly. An
alternative are comma-separated lists generated by -(port-"s$. Comma-Separated Value (CSV) files
that can then be opened in programs like Microsoft Excel allows you to continue working smoothly
with the data retrieved by PowerShell. You can then turn columns of numbers into expressive
graphics.
(ir | Export-Csv test.csv
.\test.csv
The objects returned by .ir are converted into text along with all their properties. Open the resulting
CSV file and, if you have installed Microsoft Excel, the information will be displayed column-by-
column as an Excel spreadsheet. You could also display the information in a text editor if you don't
have Excel.
While Excel can open a CSV file, but cannot identify the columns
correctly, the fault may lie with your country settings. -(port-"S8
uses as default separator the list separator "," that is internationally
customary. For example, if you're using a German system, the
Windows control panel country settings would use the not very
customary tab character as list separator. So that Excel can import comma-
separated lists correctly, you must change either the list separator character in
your regional settings or change the separator character from a comma to a tab
in the resulting CSV file:
# Make a comma-separated list
(ir | Export-Csv test1.csv
# Replace a comma by a tab respectively in this list
Get-Content test1.csv | ForEach-Object 9 $_.replace$',', "`t"& :
|
Out-File test2.csv
Table of Contents | About PowerShell Plus 148 Sponsors | Resources | BBS Technologies
# A German system will now assign columns correctly in Excel:
.\test2.csv
However, this is a case of a very simple replacement so it doesn't take into
consideration the commas that could be found in column text.
-(port-"s$ consequently takes care of the formatting data job by writing all object properties as
arrays in comma-separated files. What happens when you mess things up by using a formatting
cmdlet is shown by the next example:
(ir | Format-Table | Export-Csv test.csv
.\test.csv
The information in the CSV file is now nearly unreadable, and it becomes clear how formatting
cmdlets do their work behind the scenes by embedding objects in their own formatting instructions.
That's why you may never use formatting cmdlets if you want to use -(port-"s$ to store raw
information in a file. In general, you should also use formatting cmdlets only at the end of your
pipeline so that formatting instructions will not disrupt other commands.
A question remains: if you use formatting cmdlets to specify which of an object's properties you're
interested in, how then can you determine which properties are written into the CSV file? The
answer is to strip away the unwanted properties from the objects by using Select-,b>ect. You can
then state the property that you want to keep. All the others will be removed from the object. That's
the solution, for -(port-"s$ always writes all (remaining) properties into the CSV file:
(ir | Select-Object Name, Length, La6t7rite3ime | Export-Csv test.csv
.\test.csv
+T'L *utputs
If you'd like, PowerShell can also pack its results into (rudimentary) HTML files. Converting objects
into HTML formats is done by "on$ert6o-0t!l:
Get-Process | ConvertTo-Html | Out-File output.htm
.\output.htm
But don't be alarmed if the procedure takes a while because PowerShell has to read out all of the
objects' properties and save them as a HTML table. If you want to see only particular properties as a
HTML report, as in the case of -(port-"s$, you should never use formatting cmdlets. It would be
better for you to use Select-,b>ect here. You could also take this opportunity to give the HTML page
a title by using the -title parameter. The title will turn up later on the title bar of the browser that is
displaying your file. Unfortunately, the cmdlet doesn't have formatting options that go beyond this:
Get-Process | Select-Object Name, (e6cription |
ConvertTo-Html -title "Process Report" |
Out-File output.htm
.\output.htm
Table of Contents | About PowerShell Plus 149 Sponsors | Resources | BBS Technologies
The Extended T!pe S!stem LPart *neM
One of the PowerShell console's most remarkable capabilities is converting any object into text. You
have seen how different formatting cmdlets can turn object properties into text and output them as
text either beside or below each other.
What is striking in this connection is above all that PowerShell succeeds in only converting an
object's essential properties into text. PowerShell would have to fail right from the beginning if it had
to convert absolutely all of an object's properties into text, for then even a simple directory listing
would generate a confusing amount of information:
(ir | Format-Table * -wrap
PSPat PSPar PSChi PSDri PSPro PSIsC Mode Name Paren Exist Root Full
h entPa ldNam ve vider ontai t s Name
th e ner


----- ----- ----- ----- ----- ----- ---- ---- ----- ----- ---- ----
Micro Micro Appli C Micro True d---- Appli Tobia True C:\ C:\U
soft. soft. catio soft. catio s Wel sers
Power Power n Dat Power n Dat tner \Tob
Shell Shell a Shell a ias
.Core .Core .Core Welt
\File \File \File ner\
Syste Syste Syste Appl
m::C: m::C: m icat
\User \User ion
s\Tob s\Tob Data
ias W ias W
eltne eltne
r\App r
licit
ion D
ata
Micro Micro Backu C Micro True d---- Backu Tobia True C:\ C:\U
soft. soft. p soft. p s Wel sers
Power Power Power tner \Tob
Shell Shell Shell ias
.Core .Core .Core Welt
\File \File \File ner\
Syste Syste Syste Back
m::C: m::C: m up
\User \User
s\Tob s\Tob
ias W ias W
eltne eltne
r\Bac r
kup
(...)
Table of Contents | About PowerShell Plus 150 Sponsors | Resources | BBS Technologies
You don't have to make do with this raw, completely unserviceable text conversion of object
properties. You can convert text in a way that makes sense in a practical way by using the Extended
Type System (ETS),. Only the ETS can enable PowerShell to process internal objects, waiting until
they reach the end of the pipeline before transforming them into understandable text.
(ir
Directory: Microsoft.PowerShell.Core\FileSystem::C:\Users\
Tobias Weltner
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 01.10.2007 16:09 Application Data
d---- 26.07.2007 11:03 Backup
(...)
The ETS consists of two parts. One part takes care of formatting objects and will be described next.
The other part attends to object properties and will be explained in the next chapter.
,endering Text as Text and *nl! Text
The ETS goes into action only when objects are output in the console. The ETS does nothing if the
data is already available as text. So, if you wanted to use ,ut-Strin to convert a directory listing
into text right from the beginning and then pass it through one of the formatting cmdlets, it would
not be rendered any differently:
# Convert directory listing objects into plain text:
$text = (ir | Out-String
# All additional outputs will return the identical result,
# for text will not be converted:
$text
$text | Format-Table
$text | Format-List
Gour 3ish +as Priorit!
The ETS will still remain inactive if you specify after a formatting cmdlet like /or!at-6able which
properties should be converted into text., The conversion of objects into text is not the problem, but
the selection and differentiation of important and unimportant properties is the issue. If you specify
which properties should be converted, you won't let the ETS make this decision:
# If you specify the properties, ETS will no longer select them:
(ir | Format-Table Name, Length, La6t7rite3ime
Table of Contents | About PowerShell Plus 151 Sponsors | Resources | BBS Technologies
nown *bJects and "ormatting
If you use a formatting cmdlet like /or!at-6able without selecting properties after it, the ETS will go
into action for the first time, because the way in which these objects are to be displayed and which
properties are to be shown now must be selected automatically. To do this, the ETS first determines
what kinds of objects are to be converted into text:
(ir | ForEach-Object 9 $_.GetType$&.FullName :
.ir returns files in a Syste!.4,./ile4n7o object and files in a Syste!.4,..irectory4n7o object. Then,
the ETS looks in its own internal records to see how these objects must be converted into text. The
records are stored in the form of XML files that have the file extension ".ps1xml":
(ir $pshome\*.format.ps1xml
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 13.04.2007 19:40 22120 Certificate.format.ps1xml
-a--- 13.04.2007 19:40 60703 DotNetTypes.format.ps1xml
-a--- 13.04.2007 19:40 19730 FileSystem.format.ps1xml
-a--- 13.04.2007 19:40 250197 Help.format.ps1xml
-a--- 13.04.2007 19:40 65283 PowerShellCore.format.ps1xml
-a--- 13.04.2007 19:40 13394 PowerShellTrace.format.ps1xml
-a--- 13.04.2007 19:40 13540 Registry.format.ps1xml
Every object is precisely defined in these XML files. Among others, the definition includes which
object properties are supposed to be converted into text and whether the object should be displayed
in the form of a list or table.
The ETS runs into trouble only when you mix several object types
that don't really fit together, as is the case here:
Get-ProcessB (ir | Format-Table
H...I
out-lineoutput : ?bNect of type
"@icrosoft./o%er5hell.'ommands.
.nternal.1ormat.1ormat5tart=ata" is not legal or not in the
correct seQuence. This is liKely caused by a user-specified
"format-table" command %hich is conflicting %ith the default
formatting.
The files and directories that .ir outputs cannot be displayed by the
formatting that PowerShell uses for Processes. So, they won't allow
themselves to be mixed. One solution would be to send the objects
individually to the fitting formatter:
Get-Process | Format-TableB (ir | Format-Table
Table of Contents | About PowerShell Plus 152 Sponsors | Resources | BBS Technologies
Another solution would be not to use any formatting cmdlets at all, because
then the ETS would nose around automatically until it found the fitting format
-as you will see soon.
8nknown *bJects
If the object that the ETS is supposed to convert into text is unknown because it isn't defined in one
of the ps1xml records, the ETS will flatly convert all properties of the object into text. Then, the
question becomes whether the object is to be displayed as a table or a list. If there are fewer than
five, the ETS uses a table view, otherwise a list view. You can verify that easily enough yourself by
fabricating your own "homemade" objects:
# Create a new empty object:
$object = New-Object ;C,Mect
# Attach a new property:
Add-Member Noteropert< "a" ! -inputObject $object
# Powershell outputs the object with Format-Table and show the
# single property:
$object
a
-
1
# Add three additional properties:
Add-Member Noteropert< "b" ! -inputObject $object
Add-Member Noteropert< "c" ! -inputObject $object
Add-Member Noteropert< "d" ! -inputObject $object
# The object is still shown as a table:
$object
a b c d
- - - -
1 1 1 1
# The fifth property makes a difference:
Add-Member Noteropert< "e" ! -inputObject $object
# Now the object is converted with Format-List (properties below
# and not beside each other):
$object
a : 1
b : 1
c : 1
d : 1
e : 1
Table of Contents | About PowerShell Plus 153 Sponsors | Resources | BBS Technologies
Emergenc! 'ode
If during output the ETS discovers a critical condition, it will automatically switch over to list view.
Such a critical condition can arise, for example, when the ETS encounters unexpected objects. The
following instruction will initially output the list of running processes in table view, but because file
system objects turn up suddenly and unexpectedly, during the output the ETS switches over to
emergency mode and lines up the remaining objects in list view.
Get-ProcessB (ir
<The Case o0 the Ianished Column<
When encountering unknown objects, the ETS always takes its cue from the first object that it
outputs. That can cause a strange phenomenon. The ETS always shows all object properties for an
unknown object, but only all object properties of the 7irst object that the ETS outputs. If further
objects follow with more properties, the present selection of properties remains and information is
suppressed.
The following example shows how information can be withheld: 'et-Process returns a list of running
processes. They are sorted by the property Start6i!e and subsequently the only properties that are
output are Na!e and Start6i!e:
Get-Process | Sort-Object ;tart3ime | Select-Object Name,;tart3ime
When you execute these lines, you may possibly get a lot of error messages, but that's not your
fault. Without administrator privileges, you aren't allowed to access many processes: you can't even
ask what the start-up time was. As a result, you'll get a list of processes of which only a few are
listed with their start times. Only the process names are output. The start times of all processes is
simply suppressed. Why?
Whenever you use Select-,b>ect to take a property away from an object, you change the object
type. 'et-Process retrieves Process objects, and you cannot simply cancel the properties of these
objects. That's why Select-,b>ect wraps the information of the incoming Process objects in new
objects, which it creates new:
Get-Process | Sort-Object ;tart3ime |
Select-Object Name,;tart3ime |
ForEach-Object 9 $_.GetType$&.FullName :
System.Management.Automation.PSCustomObject
(...)
The new objects are of the PS"usto!,b>ect type. There is no entry in the ETS record for this object
type, and so the ETS outputs all the properties of the 7irst object. Because you had used Sort-,b>ect
to sort the output by ascending start times, the list begins with the objects that have no start time
because of access restrictions.
As a result, the ETS recognizes only one property, 5a!e, in the first object. It doesn't find the start
time in the first object and so start times are not output for the following objects. You can solve this
problem by not relying on the ETS, but instead selecting the object you want:
Table of Contents | About PowerShell Plus 154 Sponsors | Resources | BBS Technologies
Get-Process | Sort-Object ;tart3ime |
Select-Object Name,;tart3ime |
Format-Table Name, ;tart3ime
ETS Enhancement
If the ETS is familiar with a certain object type, it can convert it into text optimally. For unknown
objects, conversion is far less elegant, possibly even useless. Fortunately, the ETS can be enhanced:
all you need to do is to teach ETS how to handle new object types so that they, too, can be
displayed as text optimally.
Planning Enhancement
The first step of ETS enhancement is to determine which object type you want to display better. You
may frequently use 'et-W!i,b>ect to get information from the WMI service, but you're not happy
with the way PowerShell displays these objects:
Get-WmiObject 7in'2Eroce66or
__GENUS : 2
__CLASS : Win32_Processor
__SUPERCLASS : CIM_Processor
__DYNASTY : CIM_ManagedSystemElement
__RELPATH : Win32_Processor.DeviceID="CPU0"
__PROPERTY_COUNT : 48
__DERIVATION : {CIM_Processor, CIM_LogicalDevice,
CIM_LogicalElement, CIM_Managed
SystemElement}
__SERVER : TOBIASWELTNE-PC
__NAMESPACE : root\cimv2
__PATH : \\TOBIASWELTNE-PC\root\cimv2:Win32_
Processor.DeviceID="CPU0"
AddressWidth : 32
Architecture : 9
Availability : 3
Caption : x64 Family 6 Model 15 Stepping 6
ConfigManagerErrorCode :
ConfigManagerUserConfig :
CpuStatus : 1
CreationClassName : Win32_Processor
CurrentClockSpeed : 1000
CurrentVoltage : 12
DataWidth : 64
Description : x64 Family 6 Model 15 Stepping 6
DeviceID : CPU0
ErrorCleared :
ErrorDescription :
ExtClock :
Family : 1
Table of Contents | About PowerShell Plus 155 Sponsors | Resources | BBS Technologies
InstallDate :
L2CacheSize : 4096
L2CacheSpeed :
L3CacheSize : 0
L3CacheSpeed : 0
LastErrorCode :
Level : 6
LoadPercentage :
Manufacturer : GenuineIntel
MaxClockSpeed : 2167
Name : Intel(R) Core(TM)2 CPU T7400 @ 2.16GHz
NumberOfCores : 2
NumberOfLogicalProcessors : 2
OtherFamilyDescription :
PNPDeviceID :
PowerManagementCapabilities :
PowerManagementSupported : False
ProcessorId : BFEBFBFF000006F6
ProcessorType : 3
Revision : 3846
Role : CPU
SocketDesignation : U1
Status : OK
StatusInfo : 3
Stepping : 6
SystemCreationClassName : Win32_ComputerSystem
SystemName : TOBIASWELTNE-PC
UniqueId :
UpgradeMethod : 8
Version : Modell 15, Stepping 6
VoltageCaps :
First, find out what type of object is returned by the command:
$object = Get-WmiObject 7in'2Eroce66or | Select-Object -first !
$object.GetType$&.FullName
System.Management.ManagementObject
This shows you that you need an ETS enhancement for objects of the type
Syste!.Manae!ent.Manae!ent,b>ect. Next, take a look at this object's properties and select one
that you want the ETS to convert into text. For example, .e$ice4., 5a!e, and Processor4.. Then,
formulate the definition of the object in XML. In the 6able0eaders area, set column headers, and in
the 6ableRow-ntries area, set object properties.
<Configuration>
<ViewDefinitions>
<View>
<Name>CustomView</Name>
<ViewSelectedBy>
<TypeName>5ystem.@anagement.@anagement?bNect</TypeName>
</ViewSelectedBy>
Table of Contents | About PowerShell Plus 156 Sponsors | Resources | BBS Technologies
<TableControl>
<TableHeaders>
<TableColumnHeader>
<Label>+ame</Label>
<Width>12</Width>
</TableColumnHeader>
<TableColumnHeader>
<Label>=escription</Label>
<Width>30</Width>
</TableColumnHeader>
<TableColumnHeader>
<Label>.=</Label>
</TableColumnHeader>
</TableHeaders>
<TableRowEntries>
<TableRowEntry>
<TableColumnItems>
<TableColumnItem>
<PropertyName>=e#ice.=</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>=escription</PropertyName>
</TableColumnItem>
<TableColumnItem>
<PropertyName>/rocessor.=</PropertyName>
</TableColumnItem>
</TableColumnItems>
</TableRowEntry>
</TableRowEntries>
</TableControl>
</View>
</ViewDefinitions>
</Configuration>
Store this XML code in a file called WinG?3Processor.7or!at.ps1(!l.Thhen, use <pdate-/or!at.ata
to read it into the ETS:
Update-FormatData Win32_Processor.format.ps1xml
Now, the result will be much easier to understand when you output WinG?3Processorobjects again:
Get-WmiObject 7in'2Eroce66or
Name Description ID
---- ----------- --
CPU0 x64 Family 6 Model 15 Stepp... BFEBFBFF000006F6
However, in this particular instance a mishap occurred. When you acquire other WMI objects, these
will now also be displayed in the format that you just defined:
Get-WmiObject 7in'2E;hare
Table of Contents | About PowerShell Plus 157 Sponsors | Resources | BBS Technologies
Name Description ID
---- ----------- --
Remote Admin
Default share
Default share
Remote IPC
Default share
The reason has to do with special features of the WMI. It returns all WMI objects in a
Syste!.Manae!ent.Manae!ent,b>ect type.
$object = Get-WmiObject 7in'2E;er4ice | Select-Object -first !
$object.GetType$&.FullName
System.Management.ManagementObject
So, the ETS didn't make a mistake. Instead, the culprit is the WMI as for WMI objects (and only for
these), ETS enhancements must be more specific since the type name alone is not enough. That's
why WMI objects are assigned to additional object types that you can find in the PS6ype5a!es
property:
$object = Get-WmiObject 7in'2Eroce66or | Select-Object -first !
$object.PSTypeNames
System.Management.ManagementObject#root\cimv2\Win32_Processor
System.Management.ManagementObject
System.Management.ManagementBaseObject
System.ComponentModel.Component
System.MarshalByRefObject
System.Object
The object name that is specific to WinG?3Processor objects is called
System.Management.ManagementObjectNroot*ci!$?*WinG?3Processor. So, you would have to
specify this object name in your ETS enhancement so that the enhancement applies only to
WinG?3Processor WMI objects:
<Configuration>
<ViewDefinitions>
<View>
<Name>CustomView</Name>
<ViewSelectedBy>
<TypeName>5ystem.@anagement.@anagement?bNectRroot
(cim#*()in4*7/rocessor</TypeName>
</ViewSelectedBy>
<TableControl>
<TableHeaders>
<TableColumnHeader>
(...)
Modify your enhancement accordingly, and read it again with <pdate-/or!at.ata. You can safely
ignore the resulting error message. After updating, your enhancement will be valid only for
WinG?3Process WMI objects.
Table of Contents | About PowerShell Plus 158 Sponsors | Resources | BBS Technologies
Summar!
PowerShell uses a pipeline for all command entries, which feeds the results of the preceding
command directly into the subsequent command. The pipeline is active even when you enter only a
single command because PowerShell always automatically adds the ,ut-.e7ault cmdlet at the
pipeline's end so that it always results in a two-member instruction chain.
Single command results are passed as objects. The cmdlets shown in Table 5.1 can filter, sort,
compare, measure, expand, and restrict pipeline elements. All cmdlets accomplish this on the basis
of object properties. In the process, the pipeline distinguishes between sequential and streaming
modes. In streaming mode, command results are each collected, and then passed in mass onto the
next command. Which mode you use depends solely on the pipeline commands used. Output
cmdlets dispose of output. If you specify none, PowerShell automatically uses ,ut-0ost to output the
results in the console. However, you could just as well send results to a file or printer.
All output cmdlets convert objects into readable text while formatting cmdlets are responsible for
conversion. Normally, formatting cmdlets convert only the most important, but if requested, all
objects into text. The Extended Type System (ETS) helps convert objects into text. The ETS uses
internal records that specify the best way of converting a particular object type into text. If an object
type isn't in an ETS internal record, the ETS resorts to a heuristic method, which is guided by,
among other things, how many properties are contained in the unknown object.
In addition to traditional output cmdlets, export cmdlets store objects either as comma-separated
lists that can be opened in Excel or serialized in an XML format. Serialized objects can be
comfortably converted back into objects at a later time. Because when exporting, in contrast to
outputting, only plain object properties, without cosmetic formatting, are stored so that no
formatting cmdlets are used.
Table of Contents | About PowerShell Plus 159 Sponsors | Resources | BBS Technologies
CHAPTER 6.
sing !b"ects
sing !b"ects
PowerShell always works with objects. Whenever you output objects into the PowerShell console,
PowerShell automatically converts the rich objects into readable text. In this chapter, you will learn
what objects are and how to get your hands on PowerShell objects before they get converted to
simple text.
Topics Covered:
Objects = Properties + Methods
Creating a New Object
Adding Properties
Adding Methods
Properties: What an Object "Is"
Properties Containing Objects
Read-Only and Read-Write Properties
Table 6.1: Properties of the RawUI object
Property Types
Listing All Properties
Methods: What an Object "Can Do"
Eliminating "Internal" Methods
Get_ and Set_ Methods
Standard Methods
Table 6.2: Standard methods of a .NET object
Calling a Method
Call Methods with Arguments
Which Arguments are Required?
Low-Level Functions
Several Method "Signatures"
Playing with PromptForChoice
Working with Real-Life Objects
Storing Results in Variables
Using Object Properties
PowerShell-Specific Properties
Table 6.3: Different property types
Using Object Methods
Different Method Types
Table 6.4: Different types of methods
Using Static Methods
Table 6.5: Mathematical functions from the [Math] library
Finding Interesting .NET Types
Converting Object Types
Using Static Type Members
Using Dynamic Object Instance Members
Listing Assemblies
EndFinding Interesting Classes (Types)
Looking for Methods
Creating New Objects
Creating New Objects with New-Object
Table of Contents | About PowerShell Plus 160 Sponsors | Resources | BBS Technologies
Using Constructors
New Objects by Conversion
Loading Additional Assemblies: Improved Internet Download
Using COM Objects
Which COM Objects Are Available?
How Do You Use COM Objects?
Summary
*bJects O Properties 9 'ethods
In real life, you probably already know what an object is: everything you can touch. Objects in
PowerShell are actually quite similar. Let's turn a typical real-world object like a pocketknife into a
PowerShell object.
How would you describe this object to someone, let's say over a phone line? You would probably
carefully examine the object and then describe what it is and what it can do:
Properties: a pocketknife has particular properties, such as its color, manufacturer, size, or
number of blades. The object is red, weights 55 grams, has three blades, and is made by the
firm Idera. So properties describe what an object is.
'ethods: in addition, you can do things with this object, such as cut, turn screws, or pull
corks out of wine bottles. The object can cut, screw, and remove corks. Everything that an
object can is called its !ethods.
In the computing world, an object is very similar: its nature is described by properties, and the
actions it can perform are called its methods. Properties and methods are called !e!bers.
Creating a 7ew *bJect
Let's turn our real-life pocketknife into a virtual pocketknife. Using 5ew-,b>ect, PowerShell can
generate new objects, even a virtual pocketknife. First you need a new and empty object:
$pocketknife = New-Object C,Mect
This new object is actually pretty useless right now. If you call it, PowerShell will literally return
"nothing":
$pocketknife
Adding Properties
Next, let's start describing what our object is. To do that, add properties to the object.
# Adding a new property:
Add-Member -memberType Noteropert< -name Color -value @ed -inputObject
$pocketknife
Table of Contents | About PowerShell Plus 161 Sponsors | Resources | BBS Technologies
Use the Add-Me!ber cmdlet to add properties. Here, you added the property "olor with the value
Red to the object =pocketkni7e. If you call the object now, it suddenly has a first property telling the
world that its color is red:
$pocketknife
Color
-----
Red
In the same way, you now add more properties to describe the object even better. Remember that
you don't need to completely write out parameter names. It is enough to write only as much as to
make the parameter name unambiguous:
# Shorten parameter names:
Add-Member -Me Noteropert< -In $pocketknife -Na 7eight -Value %%
In fact, you don't need to specify parameter names for some of the parameters at all because some
of them are positional: provided you specify parameters in the right order, PowerShell can
automatically assign your values to the correct parameter. Adding new properties to your object
becomes even easier:
# Specify arguments without parameter names by position data:
Add-Member -inputObject $pocketknife Noteropert< +an82act8rer 1dera
Most PowerShell cmdlets can receive their input objects either by parameter (-inputObject) or via
the pipeline, so you can add properties to your object in yet another way:
# Specify "inputObject" through the pipeline:
$pocketknife | Add-Member Noteropert< )lade6 '
By now, you've described the object in =pocketkni7e with a total of four properties. If you output the
object in =pocketkni7e in the PowerShell console, PowerShell automatically converts the object into
readable text:
# Show all properties of the object all at once:
$pocketknife
Color Weight Manufacturer Blades
----- ------ ------------ -------
Red 55 Idera 3
Outputting an object to the console gets you a quick overview over its properties. To access the
value of a specific property, add a dot and then the property name:
# Display a particular property:
$pocketknife.manufacturer
Idera
Table of Contents | About PowerShell Plus 162 Sponsors | Resources | BBS Technologies
Adding 'ethods
With every new property you added to your object, =pocketkni7e has been gradually taking shape,
but it still really can't do anything. Properties only describe what an object is, not what it can do.
The actions your object can do are called its !ethods. So let's teach your object a few useful
methods:
# Adding a new method:
Add-Member -memberType ;cript+ethod -In $pocketknife `
-name c8t -Value 9 "I'm whittling now" :
# Specify arguments without parameter names by position data:
Add-Member -in $pocketknife ;cript+ethod 6cre5 9 "Phew...it's in!" :
# Specifying "InputObject" directly through the pipeline:
$pocketknife | Add-Member ;cript+ethod cor>6cre5 9 "Pop! Cheers!" :
Again, you used the Add-Me!ber cmdlet, but this time you added a method instead of a property (in
this case, a ScriptMethod). The value is a scriptblock marked by braces, which contains the
PowerShell instructions you want the method to perform. If you output your object, it will still look
the same because PowerShell only visualizes object properties, not methods:
$pocketknife
Color Weight Manufacturer Blades
----- ------ ------------ -------
Red 55 Idera 3
To use any of the three newly added methods, add a dot and then the method name followed by two
parentheses, which are what distinguish properties from methods. For example, if you'd like to
remove a cork with your virtual pocketknife, enter this instruction:
$pocketknife.corkscrew$&
Pop! Cheers!
Your object really does carry out the exact script commands you assigned to the corkscrew%&
method. So, methods perform actions, while properties merely provide information. Always
remember to add parentheses to method names. If you forget them, something interesting
happens:
# If you don't use parentheses, you'll retrieve information on a method:
$pocketknife.corkscrew
Script : "Pop! Cheers!"
OverloadDefinitions : {System.Object corkscrew();}
MemberType : ScriptMethod
TypeNameOfValue : System.Object
Value : System.Object corkscrew();
Name : corkscrew
IsInstance : True
Table of Contents | About PowerShell Plus 163 Sponsors | Resources | BBS Technologies
You just received a method description. What's interesting about this is mainly the
,$erload.e7initions property. As you'll see later, it reveals the exact way to use a command for any
method. In fact, the ,$erload.e7initions information is in an additional object. For PowerShell,
absolutely everything is an object so you could store the object in a variable and then specifically
ask the ,$erload.e7initions property for information:
# Information about a method is returned in an object of its own:
$info = $pocketknife.corkscrew
$info.OverloadDefinitions
System.Object corkscrew();
The "virtual pocketknife" example reveals that objects are containers that contain data (properties)
and actions (methods).
Our virtual pocketknife was a somewhat artificial object with no real use. Next, let's take a look at a
more interesting object which PowerShell stores in the variable =host.
Properties: 3hat an *bJect <&s<
Properties describe an object. Object properties are automatically converted into text when you
output the object to the console. That's enough to investigate any object. Check out the properties
in =host!
$host
Name : ConsoleHost
Version : 1.0.0.0
InstanceId : e32debaf-3d10-4c4c-9bc6-ea58f8f17a8f
UI : System.Management.Automation.Internal.
Host.InternalHostUserInterface
CurrentCulture : en-US
CurrentUICulture : en-US
PrivateData : Microsoft.PowerShell.ConsoleHost+ConsoleColorProxy
The object stored in the variable =host apparently contains seven properties. The properties' names
are listed in the first column. So, if you want to find out which PowerShell version you're using, you
could access and return the 8ersion property:
$host.Version
Major Minor Build Revision
----- ----- ----- --------
1 0 0 0
It works-the version is displayed. However, the version isn't displayed as a single number. Rather,
PowerShell displays four columns: Ma>or, Minor, 9uild and Re$ision. Whenever you see columns, you
know these are the object properties that PowerShell has just converted into text. Let's check out
the data type that the 8ersion property uses:
Table of Contents | About PowerShell Plus 164 Sponsors | Resources | BBS Technologies
$version = $host.Version
$version.GetType$&.FullName
System.Version
The version is not stored as a Strin object but as a Syste!.8ersion object. This object type is
perfect for storing versions, allowing you to easily read all details about any given version:
$host.Version.Major
1
$host.Version.Build
0
Knowing an object type is very useful because once you know there is a type called Syste!.8ersion,
you can use it for your own purposes as well. Try and convert a simple strin of your choice into a
rich $ersion object! To do that, simply make sure the string consists of four numbers separated by
dots (the typical format for versions), then make PowerShell convert the string into a
System.Version type. You convert things by adding the target type in square brackets in front of the
string:
FSystem.VersionG'12.55.3.28334'
Major Minor Build Revision
----- ----- ----- --------
12 55 3 28334
The "urrent"ulture property is just another example of the same concept. Read this property and
find out its type:
$host.CurrentCulture
LCID Name DisplayName
---- ---- -----------
1033 en-US English (United States)
$host.CurrentCulture.GetType$&.FullName
System.Globalization.CultureInfo
Country properties are again stored in a highly specialized type that describes a culture with the
properties L"4., 5a!e, and .isplay5a!e. If you wanted to know which international version of
PowerShell you are using, read the .isplay5a!e property:
$host.CurrentCulture.DisplayName
English (United States)
$host.CurrentCulture.DisplayName.GetType$&.FullName
Table of Contents | About PowerShell Plus 165 Sponsors | Resources | BBS Technologies
System.String
Likewise, you could convert any suitable string into a "ulture4n7o-object. So if you wanted to find out
details about the 'de-DE' locale, do this:
FSystem.Globalization.CultureInfoG'de-DE'
LCID Name DisplayName
---- ---- -----------
1031 de-DE German (Germany)
You could also convert the LCID into a "ulture4n7o object by converting a suitable number:
FSystem.Globalization.CultureInfoG!"''
LCID Name DisplayName
---- ---- -----------
1033 en-US English (United States)
Properties Containing *bJects
The properties of an object store data, and this data is, in turn, stored in various other objects. Two
properties in =host seem to be special: <4 and Pri$ate.ata. When you output $host into the console,
all other properties are converted into readable text - except for the properties UI and PrivateData:
$host
Name : ConsoleHost
Version : 1.0.0.0
InstanceId : e32debaf-3d10-4c4c-9bc6-ea58f8f17a8f
$. : [email protected].
Lost..nternalLost$ser.nterface
CurrentCulture : en-US
CurrentUICulture : en-US
/ri#ate=ata : @icrosoft./o%er5hell.'onsoleLost'onsole'olor/roxy
The reason is that both these properties contain an object that, as the only property, provides, in
turn, an object. If you'd like to find out what is actually stored in the <4 property, read the property:
$host.UI
RawUI
-----
System.Management.Automation.Internal.
Host.InternalHostRawUserInterface
You see that the property <4 contains only a single property called Raw<4, in which yet another
object is stored. Let's see what sort of object is stored in the Raw<4 property:
$host.ui.rawui
Table of Contents | About PowerShell Plus 166 Sponsors | Resources | BBS Technologies
ForegroundColor : DarkYellow
BackgroundColor : DarkMagenta
CursorPosition : 0,136
WindowPosition : 0,87
CursorSize : 25
BufferSize : 120,3000
WindowSize : 120,50
MaxWindowSize : 120,62
MaxPhysicalWindowSize : 140,62
KeyAvailable : False
WindowTitle : PowerShell
"RawUI" stands for "Raw User Interface" and exposes the raw user interface settings your
PowerShell console uses. You can read all of these properties, but can you also change them?
,ead(*nl! and ,ead(3rite Properties
Can you actually change properties, too? And if you can, what happens then?
Properties need to accurately describe an object, so if you modify a property, the underlying object
has to also be modified to reflect that change. If that is not possible, the property cannot be
changed and is called "read-only."
Console background and foreground colors are a great example of properties you can easily change.
If you do, the console will change colors accordingly. Your property changes are reflected by the
object, and the changed properties still accurately describe the object.
$host.ui.rawui.BackgroundColor = "Green"
$host.ui.rawui.ForegroundColor = "White"
Type cls so the entire console adopts this color scheme.
Other properties cannot be changed. If you try anyway, you'll get an error message:
$host.ui.rawui.keyavailable = $true
"SeyA#ailable" is a <ead?nly-property.
At line:1 char:19
:host.ui.ra%ui.K !!!! eya#ailable ; :true
Whether the console receives key press input, or not, depends on whether you pressed a key or not.
You cannot control that by changing a property, so this property refuses to be changed. You can
only read it.
Property Description
ForegroundColor Text color. Optional values are 9lack, .ark9lue,
Table of Contents | About PowerShell Plus 167 Sponsors | Resources | BBS Technologies
.ark'reen, .ark"yan, .arkRed, .arkMaenta,
.ark2ellow, 'ray, .ark'ray, 9lue, 'reen, "yan, Red,
Maenta, 2ellow, and White.
BackgroundColor
Background color. Optional values are 9lack,
.ark9lue, .ark'reen, .ark"yan, .arkRed,
.arkMaenta, .ark2ellow, 'ray, .ark'ray, 9lue,
'reen, "yan, Red, Maenta, 2ellow, and White.
CursorPosition Current position of the cursor
WindowPosition Current position of the window
CursorSize Size of the cursor
BufferSize Size of the screen buffer
WindowSize Size of the visible window
MaxWindowSize Maximally permissible window size
MaxPhysicalWindowSiz
e
Maximum possible window size
KeyAvailable Makes key press input available
WindowTitle Text in the window title bar
Table 2$#: Properties of the RawUI object
Propert! T!pes
Some properties accept numeric values. For example, the size of a blinking cursor is specified as a
number from 0 to 100 and corresponds to the fill percentage. The next line sets a cursor size of
75%. Values outside the 0-100 numeric range generate an error:
# A value from 0 to 100 is permitted:
$host.ui.rawui.cursorsize = *%
# Values outside this range will generate an error:
$host.ui.rawui.cursorsize = !"""
Table of Contents | About PowerShell Plus 168 Sponsors | Resources | BBS Technologies
Gxception setting "'ursor5ize": "'annot process "'ursor5ize"
because the cursor size specified is in#alid.
/arameter name: #alue
Actual #alue %as 1888."
At line:1 char:19
:host.ui.ra%ui.c !!!! ursorsize ; 1888
Other properties expect color settings. You cannot specify any color that comes to your mind,
though. Instead, PowerShell expects a "valid" color, and if your color is unknown, you receive an
error message listing the colors you can use:
# Colors are specified as text (in quotation marks):
$host.ui.rawui.ForegroundColor = "yellow"
# Not all colors are allowed:
$host.ui.rawui.ForegroundColor = "pink"
Gxception setting "1oreground'olor": "'annot con#ert #alue "pinK" to
type "5ystem.'onsole'olor" due to in#alid enumeration #alues. 5pecify
one of the follo%ing enumeration #alues and try again. The possible
enumeration #alues are "FlacK, =arKFlue, =arK,reen, =arK'yan, =arK<ed,
=arK@agenta, =arK"ello%, ,ray, =arK,ray, Flue, ,reen, 'yan, <ed,
@agenta, "ello%, )hite."
At line:1 char:19
:host.ui.ra%ui.1 !!!! oreground'olor ; "pinK"
If you assign an invalid value to the property /oreround"olor, the
error message lists the possible values. If you assign an invalid value
to the property "ursorSi#e, you get no hint. Why?
Every property expects a certain object type. Some object types are more
specific than others. Use 'et-Me!ber to find out which object types a given
property expects:
$host.ui.RawUI | Get-Member -memberType ropert<
TypeName: System.Management.Automation.Internal.Host.
InternalHostRawUserInterface
Name MemberType Definition
---- ---------- ----------
BackgroundColor Property System.ConsoleColor
BackgroundColor {get;set;}
BufferSize Property System.Management.Automation.
Host.Size BufferSize
{get;set;}
CursorPosition Property System.Management.Automation.
Host.Coordinates
CursorPosition {get;set;}
'ursor5ize Property 5ystem..nt4* CursorSize
{get;set;}
1oreground'olor Property 5ystem.'onsole'olor
ForegroundColor {get;set;}
Table of Contents | About PowerShell Plus 169 Sponsors | Resources | BBS Technologies
KeyAvailable Property System.Boolean
KeyAvailable {get;}
MaxPhysicalWindowSize Property System.Management.Automation.
Host.Size
MaxPhysicalWindowSize
{get;}
MaxWindowSize Property System.Management.Automation.
Host.Size MaxWindowSize
{get;}
WindowPosition Property System.Management.Automation.
Host.Coordinates
WindowPosition {get;set;}
WindowSize Property System.Management.Automation.
Host.Size WindowSize
{get;set;}
WindowTitle Property System.String WindowTitle
{get;set;}
As you see, /oreround"olor expects a Syste!."onsole"olor type. This type is
a highly specialized type, a list of possible values, a so called enumeration:
Fsystem.ConsoleColorG.IsEnum
True
Whenever a type is an enumeration, you can use a special .NET method called
'et5a!es%& to list the possible values defined in that enumeration:
FSystem.EnumG::GetNames$FSystem.ConsoleColorG&
Black
DarkBlue
DarkGreen
DarkCyan
DarkRed
DarkMagenta
DarkYellow
Gray
DarkGray
Blue
Green
Cyan
Red
Magenta
Yellow
White
If you specify anything not contained in the enumeration, the error message
will simply return the enumeration's contents.
Table of Contents | About PowerShell Plus 170 Sponsors | Resources | BBS Technologies
"ursorSi#e stores its data in a Syste!.4ntG? object, which is simply a 32bit
number. So, if you try to set the cursor size to 1000, you are actually not
violating the object boundaries because the value of 1000 can be stored in a
Syste!.4ntG? object. You get an error message anyway because of the
validation code that the "ursorSi#e property executes internally. So, whether
you get detailed error information really depends on the property's definition.
In the case of "ursorSi#e, you would receive only an indication that your value
is invalid, but not the reason why.
Sometimes, a property expects a value wrapped in a specific object. For example, if you'd like to
change the PowerShell window size, you could use the WindowSi#e property. As it turns out, the
property expects a new window size wrapped in an object of type
Syste!.Manae!ent.Auto!ation.0ost.Si#e. Where can you get an object like that?
$host.ui.rawui.WindowSize = !"",!""
Gxception setting ")indo%5ize": "'annot con#ert "5ystem.?bNectOP"
to "[email protected]"."
At line:1 char:19
:host.ui.ra%ui.) !!!! indo%5ize ; 188,188
There are a number of ways to provide specialized objects for
properties. The easiest approach: read the existing value of a
property (which will get you the object type you need), change the
result, and then write back the changes. For example, here's how
you would change the PowerShell window size to 80 x 30
characters:
$value = $host.ui.rawui.WindowSize
$value
Width Height
----- ------
110 64
$value.Width = I"
$value.Height = '"
$host.ui.rawui.WindowSize = $value
Or, you can freshly create the object you need by using New-Object:
$value = New-Object `
System.Management.Automation.Host.Size$I",'"&
$host.ui.rawui.WindowSize = $value
Or in a line:
Table of Contents | About PowerShell Plus 171 Sponsors | Resources | BBS Technologies
$host.ui.rawui.WindowSize = New-Object `
System.Management.Automation.Host.Size$I",'"&
Listing All Properties
Because properties and methods are all members of an object, 'et-Me!ber returns detailed
information about them. Let's use 'et-Me!ber to examine all properties defined in =host. To limit
'et-Me!ber to only properties, use the !e!ber6ype parameter and specify "property":
$host | Get-Member -memberType propert<
Name MemberType Definition
---- ---------- ----------
CurrentCulture Property System.Globalization.CultureInfo
CurrentCulture {get;}
CurrentUICulture Property System.Globalization.CultureInfo
CurrentUICulture {get;}
InstanceId Property System.Guid InstanceId {get;}
Name Property System.String Name {get;}
PrivateData Property System.Management.Automation.PSObject
PrivateData {get;}
UI Property System.Management.Automation.Host.
PSHostUserInterface UI {get;}
Version Property System.Version Version {get;}
In the column 5a!e, you now see all supported properties in =host. In the column .e7inition, the
property object type is listed first. For example, you can see that the 5a!e property stores a text as
Syste!.Strin type. The 8ersion property uses the Syste!.8ersion type.
At the end of each definition, braces report whether the property is read-only ({get;}) or can also be
modified ({get;set;}). You can see at a glance that all properties of the =host object are only
readable. Now, take a look at the =host.ui.rawui object:
$host.ui.rawui | Get-Member -memberType propert<
FacKground'olor /roperty 5ystem.'onsole'olor FacKground'olor
BgetDsetDE
Fuffer5ize /roperty [email protected].
5ize Fuffer5ize BgetDsetDE
'ursor/osition /roperty [email protected].
'oordinates 'ursor/osition BgetDsetDE
'ursor5ize /roperty 5ystem..nt4* 'ursor5ize BgetDsetDE
1oreground'olor /roperty 5ystem.'onsole'olor 1oreground'olor BgetDsetDE
KeyAvailable Property System.Boolean KeyAvailable {get;}
MaxPhysicalWindowSize Property System.Management.Automation.Host.Size
MaxPhysicalWindowSize {get;}
MaxWindowSize Property System.Management.Automation.Host.Size
Table of Contents | About PowerShell Plus 172 Sponsors | Resources | BBS Technologies
MaxWindowSize {get;}
)indo%/osition /roperty [email protected].
'oordinates )indo%/osition BgetDsetDE
)indo%5ize /roperty [email protected]
)indo%5ize BgetDsetDE
)indo%Title /roperty 5ystem.5tring )indo%Title BgetDsetDE
This result is more differentiated. It shows you that some properties could be changed, while others
could not.
There are different "sorts" of properties. Most properties are of the
Property type, but PowerShell can add additional properties like
ScriptProperty. So if you really want to list all properties, use the
!e!ber6ype parameter and assign it a value of +Property. The
wildcard in front of "property" will also select all specialized
properties like "ScriptProperty".
'ethods: 3hat an *bJect <Can )o<
Methods are things that an object can do. When you output an object to the console, only its
properties are converted into readable text. Its methods remain invisible. To list the methods of an
object, use 'et-Me!ber and use the parameter "memberType" with the value "method":
$host | Get-Member -memberType +ethod
Name MemberType Definition
---- ---------- ----------
EnterNestedPrompt Method System.Void EnterNestedPrompt()
Equals Method System.Boolean Equals(Object obj)
ExitNestedPrompt Method System.Void ExitNestedPrompt()
GetHashCode Method System.Int32 GetHashCode()
GetType Method System.Type GetType()
get7'urrent'ulture @ethod 5ystem.,lobalization.'ulture.nfo
get7'urrent'ultureHI
get7'urrent$.'ulture @ethod 5ystem.,lobalization.'ulture.nfo
get7'urrent$.'ultureHI
get7.nstance.d @ethod 5ystem.,uid get7.nstance.dHI
get7+ame @ethod 5ystem.5tring get7+ameHI
get7/ri#ate=ata @ethod [email protected]./5?bNect
get7/ri#ate=ataHI
get7$. @ethod [email protected].
/5Lost$ser.nterface get7$.HI
get7Version @ethod 5ystem.Version get7VersionHI
NotifyBeginApplication Method System.Void NotifyBeginApplication()
NotifyEndApplication Method System.Void NotifyEndApplication()
SetShouldExit Method System.Void SetShouldExit(Int32
exitCode)
Table of Contents | About PowerShell Plus 173 Sponsors | Resources | BBS Technologies
ToString Method System.String ToString()
Eliminating <&nternal< 'ethods
'et-Me!ber lists all methods defined by an object. Not all of them are really useful to you. Let's
check out why some of the listed methods are really only of limited use.
BetP and SetP 'ethods
Any method that starts with "get_" is really a method to retrieve a property value. So the method
"get_someInfo()" is getting you the very same information you could also have retrieved with the
"someInfo" property.
# Query property:
$host.version
Major Minor Build Revision
----- ----- ----- --------
1 0 0 0
# Query property value using getter method:
$host.get_Version$&
Major Minor Build Revision
----- ----- ----- --------
1 0 0 0
The same is true for Set3 methods: they change a property value and exist for properties that are
read/writeable. Note in this example: all properties of the =host object can only be read so there are
no Set_ methods. There can be more internal methods like this, such as Add_ and Remove_
methods. Generally speaking, when a method name contains an underscore, it most likely is an
internal method.
Standard 'ethods
In addition, nearly every object contains a number of "inherited" methods that are also not specific
to the object but perform general tasks for every object:
Method Description
Equals Verifies whether the object is identical to a comparison object
GetHashCode Retrieves an object's digital "fingerprint"
Table of Contents | About PowerShell Plus 174 Sponsors | Resources | BBS Technologies
GetType Retrieves the underlying object type
ToString Converts the object into readable text
Table 2$%: Standard methods of a .NET object
To sort out all methods that contain an underscore, you could use Where-,b>ect and the comparison
operator -notlike:
$host | Get-Member -memberType *method |
Where-Object 9 $_.Name -notlike '*_*' :
The =host object really only contains these unique and useful methods:
Name MemberType Definition
---- ---------- ----------
EnterNestedPrompt Method System.Void EnterNestedPrompt()
ExitNestedPrompt Method System.Void ExitNestedPrompt()
NotifyBeginApplication Method System.Void NotifyBeginApplication()
NotifyEndApplication Method System.Void NotifyEndApplication()
SetShouldExit Method System.Void SetShouldExit(Int32
exitCode)
Calling a 'ethod
Watch out: be7ore you invoke a method: make sure you know what the method will do. Methods are
commands that do something, and what a command does can be dangerous. To call a method, add
a dot to the object and then the method name. Add an opened and closed parenthesis, like this:
$host.EnterNestedPrompt$&
The PowerShell prompt changes to ">>". You have used -nter5estedPro!pt%& to open a nested
prompt. Nested prompts are not especially useful in a normal console, so exit it again using the e(it
command or call =host.-(it5estedPro!pt%&.
Nested prompts are very useful in functions or scripts because they work like breakpoints and can
temporarily stop a function or script so you can verify variable contents or make code changes, after
which you continue the code by entering e(it. You'll learn more about this in Chapter 11.
Call 'ethods with Arguments
There are a bunch of useful methods in the <4 object. Here's how you get a good overview:
$host.ui | Get-Member -memberType +ethod
Table of Contents | About PowerShell Plus 175 Sponsors | Resources | BBS Technologies
TypeName: System.Management.Automation.Internal.Host.
InternalHostUserInterface
Name MemberType Definition
---- ---------- ----------
Equals Method System.Boolean Equals(Object obj)
GetHashCode Method System.Int32 GetHashCode()
GetType Method System.Type GetType()
get_RawUI Method System.Management.Automation.Host.
PSHostRawUserInterface get_RawUI()
/rompt @ethod 5ystem.'ollections.,eneric.=ictionary
T*OO5ystem.5tring, mscorlib, ...
/rompt1or'hoice @ethod 5ystem..nt4* /rompt1or'hoiceH5tring
caption, 5tring message, ...
/rompt1or'redential @ethod [email protected].
/5'redential /rompt1or'redential...
<ead6ine @ethod 5ystem.5tring <ead6ineHI
<ead6ineAs5ecure5tring @ethod 5ystem.5ecurity.5ecure5tring
<ead6ineAs5ecure5tringHI
ToString Method System.String ToString()
)rite @ethod 5ystem.Void )riteH5tring #alueI,
5ystem.Void )riteH'onsole'olor...
)rite=ebug6ine @ethod 5ystem.Void )rite=ebug6ineH5tring
messageI
)riteGrror6ine @ethod 5ystem.Void )riteGrror6ineH5tring
#alueI
)rite6ine @ethod 5ystem.Void )rite6ineHI, 5ystem.Void
)rite6ineH5tring #alueI...
)rite/rogress @ethod 5ystem.Void )rite/rogressH.nt92
source.d, /rogress<ecord recordI
)riteVerbose6ine @ethod 5ystem.Void )riteVerbose6ineH5tring
messageI
)rite)arning6ine @ethod 5ystem.Void )rite)arning6ineH5tring
messageI
Most methods require additional arguments from you, which are listed in the .e7inition column.
3hich Arguments are ,eKuiredN
Pick out a method from the list, and ask 'et-Me!ber to get you more info. Let's pick
Write.ebuLine%&:
# Ask for data on the WriteDebugLine method
# in $host.ui:
$info = $host.UI | Get-Member 7rite(e,8gLine
# $info contains all the data on this method:
$info
TypeName: System.Management.Automation.
Internal.Host.InternalHostUserInterface
Name MemberType Definition
---- ---------- ----------
Table of Contents | About PowerShell Plus 176 Sponsors | Resources | BBS Technologies
WriteDebugLine Method System.Void
WriteDebugLine
(String message)
# Definition shows which arguments are required
# and which result will be returned:
$info.Definition
System.Void WriteDebugLine(String message)
The .e7inition property tells you how to call the method. Every definition starts with the object type
that a method returns. In this example it is Syste!.8oid, a special object type because it represents
"nothing": the method doesn't return anything at all. A method "returning" Syste!.8oid is really a
procedure, not a function.
Next, a method's name follows, which is then followed by required arguments. Write.ebuLine
needs exactly one argument called !essae, which is of Strin type. Here is how you call
Write.ebuLine%&:
$host.ui.WriteDebugLine$"Hello!"&
Hello!
Low(Level "unctions
Write.ebuLine%& really does nothing spectacular. In fact, most methods found in the =host object
are really only low-level commands used by the standard PowerShell cmdlets. For example, you
could also have output the debug notification by using the following cmdlet:
Write-Debug "Hello!"
However, there are differences: No matter what-Write.ebu6e(t%& always writes out yellow debug
messages. The high-level Write-.ebu cmdlet only outputs the debug message when the
=.ebuPre7erence variable is set to anything other than "SilentlyContinue" (which is the default).
The same applies to the Write-rrorLine, Write8erboseLine, and WriteWarninLine methods, which
are the low-level functions for the Write--rror, Write-8erbose, and Write-Warnin cmdlets.
So, if you'd like to output error or warning messages that are independent of the various preference
settings in PowerShell, use the low-level commands in =host.<4.Raw<4 instead of the cmdlets.
Several 'ethod <Signatures<
Some methods accept different argument types or even different numbers of arguments. To find out
which "signatures" a method supports, use 'et-Me!ber again and look at the .e7inition property:
$info = $host.UI | Get-Member 7riteLine
$info.Definition
Table of Contents | About PowerShell Plus 177 Sponsors | Resources | BBS Technologies
System.Void WriteLine(),
System.Void WriteLine(String value),
System.Void WriteLine(
ConsoleColor foregroundColor,
ConsoleColor backgroundColor,
String value)
Unfortunately, the definition is hard to read at first. Make it more readable by using Replace%& to add
line breaks.
Remember the strange "backtick" character ("`"). It introduces
special characters; "`n" stands for a line break.
$info.Definition.Replace$"), ", ")`n"&
System.Void WriteLine()
System.Void WriteLine(String value)
System.Void WriteLine(
ConsoleColor foregroundColor,
ConsoleColor backgroundColor,
String value)
This definition tells you: You do not necessarily need to supply arguments:
$host.ui.WriteLine$&
The result is an empty line.
To output text, you specify one argument only, the text itself:
$host.ui.WriteLine$"Hello world!"&
Hello world!
The third variant adds support for foreground and background colors:
$host.ui.WriteLine$"Red", "White", "Alarm!"&
WriteLine%& actually is the low-level function of the Write-0ost cmdlet:
Write-Host
Write-Host "Hello World!"
Write-Host -foregroundColor @ed `
-backgroundColor 7hite .larmN
Table of Contents | About PowerShell Plus 178 Sponsors | Resources | BBS Technologies
Pla!ing with Prompt"orChoice
Most methods you examined so far turned out to be low-level commands for cmdlets. This is also
true for the following methods: Write%& (corresponds to Write-0ost -no5ewLine) or
ReadLine%&/ReadLineAsSecureStrin%& (Read-0ost -asSecureStrin) or Pro!pt/or"redential%& ('et-
"redential).
A new functionality is exposed by the method Pro!pt/or"hoice%&. Let's first examine which
arguments this method expects:
$info = $host.UI | Get-Member rompt/orChoice
$info.Definition
System.Int32 PromptForChoice(String caption,
String message, Collection`1 choices,
Int32 defaultChoice)
You can get the same information if you call the method without
parentheses:
$host.ui.PromptForChoice
MemberType : Method
OverloadDefinitions : {System.Int32 PromptForChoice(
String caption, String message,
Collection`1 choices,
Int32 defaultChoice)}
TypeNameOfValue : System.Management.Automation.PSMethod
Value : System.Int32 PromptForChoice(
String caption, String message,
Collection`1 choices,
Int32 defaultChoice)
Name : PromptForChoice
IsInstance : True
The definition reveals that this method returns a numeric value (Syste!.4ntG?). It requires a
heading and a message respectively as text (Strin). The third argument is a bit strange:
"ollectionO1 choices. The fourth argument is a number (4ntG?), the standard selection. You should
have noticed by now the limitations of PowerShell's built-in description.
This is how you could use Pro!pt/or"hoice%& to create a simple menu:
$yes = $FSystem.Management.Automation.Host.ChoiceDescriptionG"&yes"&
$no = $FSystem.Management.Automation.Host.ChoiceDescriptionG"&no"&
$selection = FSystem.Management.Automation.Host.ChoiceDescriptionFGG `
$$yes,$no&
$answer = $host.ui.PromptForChoice$'Reboot',
'May the system now be rebooted?',$selection,!&
Table of Contents | About PowerShell Plus 179 Sponsors | Resources | BBS Technologies
$selectionF$answerG
if $$selection -eq "& 9
"Reboot"
: else 9
"OK, then not"
:
3orking with ,eal(Li0e *bJects
Every single PowerShell command returns objects, which is a good thing. However, it is not that
easy to get your hands on objects because whenever objects hit the PowerShell console, they will be
converted to text and lose a lot of their information.
Storing ,esults in Iariables
Do not output command results to the console to prevent PowerShell from converting objects into
simple strings. The console is a hostile place for objects because anything output to the PowerShell
console will end up as text. Instead, save the command result in a variable, which is a safe place for
objects.
$listing = (ir
However, variables are only safe places for objects until you dump their content to the console: the
objects stored inside of your variable would again be converted to text.
$listing
Directory: Microsoft.PowerShell.Core\FileSystem::
C:\Users\Tobias Weltner
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 04.03.2009 11:37 Application Data
d---- 05.03.2009 11:03 Backup
d-r-- 13.02.2009 15:05 Contacts
d---- 28.01.2009 18:33 Debug
(...)
So, to get in touch with the real objects, you can directly access them inside of a variable. .ir has
stored its directory listing in =listin. Since the listing consists of more than one entry, it is wrapped
in an array. Access an array element to get your hands on a real object:
# Access first element in listing
$object = $listingF"G
# Object is converted into text when you output it in the console
$object
Directory: Microsoft.PowerShell.Core\FileSystem::
C:\Users\Tobias Weltner
Mode LastWriteTime Length Name
Table of Contents | About PowerShell Plus 180 Sponsors | Resources | BBS Technologies
---- ------------- ------ ----
d---- 0.07.2007 11:37 Application Data
The object picked here happens to match the folder Application .ata; so it represents a directory. If
you would prefer to directly pick a particular directory or file, you can do this:
# Address a particular file:
$object = (ir c:\autoexec.bat
# Address the Windows directory:
$object = Get-Item $env:winDir
8sing *bJect Properties
You can now use 'et-Me!ber again to produce a list of all available properties:
# $object is a fully functional object that
# describes the "Application Data" directory
# First, list all object properties:
$object | Get-Member -memberType *propert<
TypeName: System.IO.DirectoryInfo
Name MemberType Definition
---- ---------- ----------
PSChildName NoteProperty System.String PSChildName=
Application Data
PSDrive NoteProperty System.Management.Automation.
PSDriveInfo PSDrive=C
PSIsContainer NoteProperty System.Boolean PSIsContainer=
True
PSParentPath NoteProperty System.String PSParentPath=
Microsoft.PowerShell.Core\
FileSystem::C:\Users...
PSPath NoteProperty System.String PSPath=Microsoft.
PowerShell.Core\FileSystem::
C:\Users\Tobia...
PSProvider NoteProperty System.Management.Automation.
ProviderInfo PSProvider=
Microsoft.PowerShell...
Attributes Property System.IO.FileAttributes
Attributes {get;set;}
CreationTime Property System.DateTime CreationTime
{get;set;}
CreationTimeUtc Property System.DateTime CreationTimeUtc
{get;set;}
Exists Property System.Boolean Exists {get;}
Extension Property System.String Extension {get;}
FullName Property System.String FullName {get;}
LastAccessTime Property System.DateTime LastAccessTime
{get;set;}
LastAccessTimeUtc Property System.DateTime LastAccessTimeUtc
{get;set;}
Table of Contents | About PowerShell Plus 181 Sponsors | Resources | BBS Technologies
LastWriteTime Property System.DateTime LastWriteTime
{get;set;}
LastWriteTimeUtc Property System.DateTime LastWriteTimeUtc
{get;set;}
Name Property System.String Name {get;}
Parent Property System.IO.DirectoryInfo
Parent {get;}
Root Property System.IO.DirectoryInfo
Root {get;}
Mode ScriptProperty System.Object Mode
{get=$catr = "";...
Properties marked with {etKsetK} in the column .e7inition may also be modified:
# Determine last access date:
$object.LastAccessTime
Wednesday, January 14, 2009 11:37:39
# Change Date:
$object.LastAccessTime = Get-Date
# Change was accepted:
$object.LastAccessTime
Saturday, March 7, 2009 15:31:41
PowerShell(Speci0ic Properties
PowerShell can add additional properties to an object. Whenever that occurs, 'et-Me!ber labels the
property accordingly in the MemberType column. Native properties are just called "Property."
Properties added by PowerShell use a prefix, such as "ScriptProperty" or "NoteProperty."
A 5oteProperty like PS"hild5a!e contains static data. PowerShell adds it to tag additional
information to an object. A ScriptProperty like Mode executes PowerShell script code that calculates
the property's value.
If you want to see the script code being executed when you call the ScriptProperty Mode, ask 'et-
Me!ber to list the property definition:
$info = $object | Get-Member +ode
$info.Definition
System.Object Mode {get=$catr = "";
if ( $this.Attributes -band 16 ) { $catr += "d" }
else { $catr += "z" };
if ( $this.Attributes -band 32 ) { $catr += "a" }
else { $catr += "-" };
if ( $this.Attributes -band 1 ) { $catr += "r" }
else { $catr += "-" };
if ( $this.Attributes -band 2 ) { $catr += "h" }
Table of Contents | About PowerShell Plus 182 Sponsors | Resources | BBS Technologies
else { $catr += "-" };
if ( $this.Attributes -band 4 ) { $catr += "s" }
else { $catr += "-" };
$catr;}
As it turns out, Mode evaluates the native Attributes property which is a bitmask. Binary bitmasks
are hard to read so that is why the new Mode script property converts the binary information into a
more user friendly format.
MemberType Description
AliasProperty Alternative name for a property that already exists
CodeProperty Static .NET method returns property contents
Property Genuine property
NoteProperty Subsequently added property with set data value
ScriptProperty
Subsequently added property whose value is calculated
by a script
ParameterizedPropert
y
Property requiring additional arguments
Table 2$-: Different property types
8sing *bJect 'ethods
Use 'et-Me!ber again to find out the methods that an object supports:
# List all methods of the object:
$object | Get-Member -memberType *method
TypeName: System.IO.DirectoryInfo
Name MemberType Definition
---- ---------- ----------
Create Method System.Void Create(),
System.Void Create(
DirectorySecurity DirectoryS...
CreateObjRef Method System.Runtime.Remoting.ObjRef
CreateObjRef(Type requestedType)
CreateSubDirectory Method System.IO.DirectoryInfo
Table of Contents | About PowerShell Plus 183 Sponsors | Resources | BBS Technologies
CreateSubDirectory(String path),
System.IO.Di...
Delete Method System.Void Delete(), System.Void
Delete(Boolean recursive)
GQuals @ethod 5ystem.Foolean GQualsH?bNect obNI
GetAccessControl Method System.Security.AccessControl.
DirectorySecurity GetAccessCo...
GetDirectories Method System.IO.DirectoryInfo[]
GetDirectories(), System.IO.
DirectoryInfo[]...
GetFiles Method System.IO.FileInfo[] GetFiles(
String searchPattern), System.IO.
FileIn...
GetFileSystemInfos Method System.IO.FileSystemInfo[]
GetFileSystemInfos(String
searchPattern), ...
,etLash'ode @ethod 5ystem..nt4* ,etLash'odeHI
GetLifetimeService Method System.Object GetLifetimeService()
GetObjectData Method System.Void GetObjectData(
SerializationInfo info,
StreamingContext co...
,etType @ethod 5ystem.Type ,etTypeHI
get7Attributes @ethod 5ystem..?.1ileAttributes
get7AttributesHI
get7'reationTime @ethod 5ystem.=ateTime get7'reationTimeHI
get7'reationTime$tc @ethod 5ystem.=ateTime get7'reationTime$tcHI
get7Gxists @ethod 5ystem.Foolean get7GxistsHI
get7Gxtension @ethod 5ystem.5tring get7GxtensionHI
get71ull+ame @ethod 5ystem.5tring get71ull+ameHI
get76astAccessTime @ethod 5ystem.=ateTime get76astAccessTimeHI
get76astAccessTime$tc @ethod 5ystem.=ateTime
get76astAccessTime$tcHI
get76ast)riteTime @ethod 5ystem.=ateTime get76ast)riteTimeHI
get76ast)riteTime$tc @ethod 5ystem.=ateTime
get76ast)riteTime$tcHI
get7+ame @ethod 5ystem.5tring get7+ameHI
get7/arent @ethod 5ystem..?.=irectory.nfo get7/arentHI
get7<oot @ethod 5ystem..?.=irectory.nfo get7<ootHI
InitializeLifetimeService Method System.Object
InitializeLifetimeService()
MoveTo Method System.Void MoveTo(String destDirName)
Refresh Method System.Void Refresh()
SetAccessControl Method System.Void SetAccessControl(
DirectorySecurity DirectorySecurity)
set7Attributes @ethod 5ystem.Void set7AttributesH
1ileAttributes #alueI
set7'reationTime @ethod 5ystem.Void set7'reationTimeH
=ateTime #alueI
set7'reationTime$tc @ethod 5ystem.Void set7'reationTime$tcH
=ateTime #alueI
set76astAccessTime @ethod 5ystem.Void set76astAccessTimeH
=ateTime #alueI
set76astAccessTime$tc @ethod 5ystem.Void set76astAccessTime$tcH
Table of Contents | About PowerShell Plus 184 Sponsors | Resources | BBS Technologies
=ateTime #alueI
set76ast)riteTime @ethod 5ystem.Void set76ast)riteTimeH
=ateTime #alueI
set76ast)riteTime$tc @ethod 5ystem.Void set76ast)riteTime$tcH
=ateTime #alueI
To5tring @ethod 5ystem.5tring To5tringHI
Again, standard methods are displayed in bold font so you can
safely ignore them because they exist in every object or match
properties.
You can apply methods just like you did in the previous examples. For example, use the
"reateSub.irectory method if you'd like to create a new subdirectory. Find out first which arguments
this method requires and what it returns:
$info = $object | Get-Member Create;8,(irector<
$info.Definition.Replace$"), ", ")`n"&
System.IO.DirectoryInfo CreateSubDirectory(String path)
System.IO.DirectoryInfo CreateSubDirectory(String path,
DirectorySecurity DirectorySecurity)
You can see that the method has two signatures. Use the first to create a subdirectory and the
second to add access permissions.
The next line creates a subdirectory called "My New Directory" without any special access privileges:
$object.CreateSubDirectory$"My New Directory"&
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 03.07.200915:49 My New Directory
Because the method returns a .irectory4n7o object as result and you haven't caught and stored this
object in a variable, the pipeline converts it into text and outputs it. You could just as well have
stored the result of the method in a variable:
$subdirectory = $object.CreateSubDirectory$"Another subdirectory"&
$subdirectory.CreationTime = "September 1, 1980"
$subdirectory.CreationTime
Monday, September 1, 1980 00:00:00
)i00erent 'ethod T!pes
Similarly to properties, PowerShell can also add additional methods to an object.
Table of Contents | About PowerShell Plus 185 Sponsors | Resources | BBS Technologies
MemberType Description
CodeMethod Method mapped to a static .NET method
Method Genuine method
ScriptMethod Method invokes PowerShell code
Table 2$/: Different types of methods
8sing Static 'ethods
By now, you know that PowerShell stores information in objects, and objects always have a type.
You know that simple text is stored in objects of type Syste!.Strin and that a date, for example, is
stored in an object of type Syste!..ate6i!e. You also know by now that each .NET object has a
'et6ype%& method with a FullName property which tells you the name of the type this object was
derived from:
$date = Get-Date
$date.GetType$&.FullName
System.DateTime
Every type can have its own set of private members called "static" members. Simply specify a type
in square brackets, then pipe it to 'et-Me!ber and use the -static parameter to see the static
members of a type.
FSystem.DateTimeG | Get-Member -static -memberType *method
TypeName: System.DateTime
Name MemberType Definition
---- ---------- ----------
Compare Method static System.Int32 Compare(
DateTime t1, DateTime t2)
DaysInMonth Method static System.Int32 DaysInMonth(
Int32 year, Int32 month)
Equals Method static System.Boolean Equals(
DateTime t1, DateTime t2),
static Sys...
FromBinary Method static System.DateTime FromBinary(
Int64 dateData)
FromFileTime Method static System.DateTime
FromFileTime(Int64 fileTime)
FromFileTimeUtc Method static System.DateTime
FromFileTimeUtc(Int64 fileTime)
Table of Contents | About PowerShell Plus 186 Sponsors | Resources | BBS Technologies
FromOADate Method static System.DateTime FromOADate(
Double d)
get_Now Method static System.DateTime get_Now()
get_Today Method static System.DateTime get_Today()
get_UtcNow Method static System.DateTime get_UtcNow()
IsLeapYear Method static System.Boolean IsLeapYear(
Int32 year)
op7Addition @ethod static 5ystem.=ateTime
op7AdditionH=ateTime d,
Time5pan tI
op7GQuality @ethod static 5ystem.Foolean
op7GQualityH=ateTime d1,
=ateTime d*I
op7,reaterThan @ethod static 5ystem.Foolean
op7,reaterThanH=ateTime t1,
=ateTime t*I
op7,reaterThan?rGQual @ethod static 5ystem.Foolean
op7,reaterThan?rGQualH=ateTime t1,
=ateTime t*I
op7.neQuality @ethod static 5ystem.Foolean
op7.neQualityH=ateTime d1,
=ateTime d*I
op76essThan @ethod static 5ystem.Foolean
op76essThanH=ateTime t1,
=ateTime t*I
op76essThan?rGQual @ethod static 5ystem.Foolean
op76essThan?rGQualH=ateTime t1,
=ateTime t*I
op75ubtraction @ethod static 5ystem.=ateTime
op75ubtractionH=ateTime d,
Time5pan tI, sta...
Parse Method static System.DateTime
Parse(String s), static System.
DateTime Par...
ParseExact Method static System.DateTime
ParseExact(String s, String
format, IFormat...
ReferenceEquals Method static System.Boolean
ReferenceEquals(Object objA,
Object objB)
SpecifyKind Method static System.DateTime
SpecifyKind(DateTime value,
DateTimeKind kind)
TryParse Method static System.Boolean
TryParse(String s, DateTime&
result), static...
TryParseExact Method static System.Boolean
TryParseExact(String s,
String format, IFo...
Table of Contents | About PowerShell Plus 187 Sponsors | Resources | BBS Technologies
There are a lot of method names starting with "op_," with "op"
standing for "operator." These are methods called internally
whenever you use this data type with an operator.
op3'reater6han,r-:ual is the method that does the internal work
when you use the PowerShell comparison operator "-ge" with date
values.
The Syste!..ate6i!e class supplies you with a bunch of important date and time methods. For
example, to convert a date string into a real DateTime object and use the current locale, use
Parse%&:
FSystem.DateTimeG::Parse$"March 12, 1999"&
Friday, March 12, 1999 00:00:00
You could easily find out whether a certain year is a leap year:
FSystem.DateTimeG::isLeapYear$2"!"&
False
2or $$x=2"""B $x -lt 2"!"B $x++& 9
if$ FSystem.DateTimeG::isLeapYear$$x& &
9 "$x is a leap year!" : :
2000 is a leap year!
2004 is a leap year!
2008 is a leap year!
Or you'd like to tell your children with absolute precision how much time will elapse before they get
their Christmas gifts:
FDateTimeG"12/24/2007 18:00" - FDateTimeG::now
Days : 74
Hours : 6
Minutes : 28
Seconds : 49
Milliseconds : 215
Ticks : 64169292156000
TotalDays : 74.2700140694444
TotalHours : 1782,48033766667
TotalMinutes : 106948,82026
TotalSeconds : 6416929,2156
TotalMilliseconds : 6416929215,6
Two dates are being subtracted from each other here so you now know what happened during this
operation:
Table of Contents | About PowerShell Plus 188 Sponsors | Resources | BBS Technologies
The first time indication is actually text. For it to become a .ate6i!e object, you must specify
the desired object type in square brackets. 4!portant) "on$ertin a Strin to a .ate6i!e this
way always uses the <S locale. 6o con$ert a Strin to a .ate6i!e usin your current locale@
use the Parse%& !ethod as shown a couple o7 !o!ents aoP
The second time comes from the 5ow static property, which returns the current time as
.ate6i!e object. This is the same as calling the 'et-.ate cmdlet (which you'd then need to
put in parenthesis because you wouldn't want to subtract the 'et-.ate cmdlet but rather the
result of the 'et-.ate cmdlet).
The two timestamps are subtracted from each other using the subtraction operator ("-"). This
was possible because the .ate6i!e class defined the op3Subtraction%& static method, which
is needed for this operator.
Of course, you could have called the static method yourself and received the same result:
FDateTimeG::op_Subtraction$"12/24/2007 18:00", FDateTimeG::Now&
Now it's your turn. In the Syste!.Math class, you'll find a lot of useful mathematical methods. Try to
put some of these methods to work.
Function Description Example
Abs
Returns the absolute value of a
specified number (without signs).
[Math]::Abs(-5)
Acos
Returns the angle whose cosine is the
specified number.
[Math]::Acos(0.6)
Asin
Returns the angle whose sine is the
specified number.
[Math]::Asin(0.6)
Atan
Returns the angle whose tangent is the
specified number.
[Math]::Atan(90)
Atan2
Returns the angle whose tangent is the
quotient of two specified numbers.
[Math]::Atan2(90,
15)
BigMul
Calculates the complete product of two
32-bit numbers.
[Math]::BigMul(1gb,
6)
Ceiling
Returns the smallest integer greater
than or equal to the specified number.
[Math]::Ceiling(5.7)
Cos
Returns the cosine of the specified
angle.
[Math]::Cos(90)
Table of Contents | About PowerShell Plus 189 Sponsors | Resources | BBS Technologies
Cosh
Returns the hyperbolic cosine of the
specified angle.
[Math]::Cosh(90)
DivRem
Calculates the quotient of two numbers
and returns the remainder in an output
parameter.
$a = 0
[Math]::DivRem(10,3
,[ref]$a)
$a
Exp
Returns the specified power of e
(2.7182818).
[Math]::Exp(12)
Floor
Returns the largest integer less than or
equal to the specified number.
[Math]::Floor(5.7)
IEEERemainde
r
Returns the remainder of division of
two specified numbers.
[Math]::IEEERemaind
er(5,2)
Log
Returns the natural logarithm of the
specified number.
[Math]::Log(1)
Log10
Returns the base 10 logarithm of the
specified number.
[Math]::Log10(6)
Max
Returns the larger of two specified
numbers.
[Math]::Max(-5, 12)
Min
Returns the smaller of two specified
numbers.
[Math]::Min(-5, 12)
Pow
Returns a specified number raised to
the specified power.
[Math]::Pow(6,2)
Round
Rounds a value to the nearest integer
or to the specified number of decimal
places.
[Math]::Round(5.51)
Sign
Returns a value indicating the sign of a
number.
[Math]::Sign(-12)
Sin Returns the sine of the specified angle. [Math]::Sin(90)
Sinh Returns the hyperbolic sine of the [Math]::Sinh(90)
Table of Contents | About PowerShell Plus 190 Sponsors | Resources | BBS Technologies
specified angle.
Sqrt
Returns the square root of a specified
number.
[Math]::Sqrt(64)
Tan
Returns the tangent of the specified
angle.
[Math]::Tan(45)
Tanh
Returns the hyperbolic tangent of the
specified angle.
[Math]::Tanh(45)
Truncate
Calculates the integral part of a
number.
[Math]::Truncate(5.6
7)
Table 2$1: Mathematical functions from the [Math] library
"inding &nteresting $7ET T!pes
The .NET framework consists of thousands of types, and maybe you are getting hungry for more.
Are there other interesting types? There are actually plenty! Here are the three things you can do
with .NET types:
Converting *bJect T!pes
For example, use System.Net.IPAddress to work with IP addresses. This is an example of a .NET
type conversion where a string is converted into a Syste!.5et.4PAddress type:
Fsystem.Net.IPAddressG'127.0.0.1'
IPAddressToString : 127.0.0.1
Address : 16777343
AddressFamily : InterNetwork
ScopeId :
IsIPv6Multicast : False
IsIPv6LinkLocal : False
IsIPv6SiteLocal : False
8sing Static T!pe 'embers
Or use Syste!.5et..5S to resolve host names. This is an example of accessing a static type method
like 'et0ost9yAddress%&:
Table of Contents | About PowerShell Plus 191 Sponsors | Resources | BBS Technologies
Fsystem.Net.DnsG::GetHostByAddress$"127.0.0.1"&
HostName Aliases AddressList
-------- ------- -----------
PCNEU01 {} {127.0.0.1}
8sing )!namic *bJect &nstance 'embers
Or you can derive an instance of a type and use its dynamic members. For example, to download a
file from the Internet:
# Download address of a file:
$address = "http://powershell.com/cs/media/p/467/download.aspx"
# Save the file to this location:
$target = "$home\chart_drive_space.V2.ps1"
# Carry out download:
$object = New-Object Net.WebClient
$object.DownloadFile$$address, $target&
"File was downloaded!"
Listing Assemblies
The search for interesting types begins with assemblies as they contain the types. First, you need to
get a list of all the assemblies that PowerShell has loaded. Use the AppDomain type to find out the
loaded assemblies. Its "urrent.o!ain%& static method will give you access to the internal PowerShell
.NET framework where you'll find the 'etAsse!blies%& dynamic method, which will enable you to get
a list of the loaded assemblies:
FAppDomainG::CurrentDomain
FriendlyName : DefaultDomain
Id : 1
ApplicationDescription :
BaseDirectory : C:\WINDOWS\system32\WindowsPowerShell\v1.0\
DynamicDirectory :
RelativeSearchPath :
SetupInformation : System.AppDomainSetup
ShadowCopyFiles : False
[AppDomain]::CurrentDomain.GetAssemblies()
GAC Version Location
--- ------- --------
True v2.0.50727 C:\Windows\Microsoft.NET\Framework\
v2.0.50727\mscorlib.dll
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\Microsoft.
PowerShell.ConsoleHost\...
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\System\
2.0.0.0__b77a5c561934e089\...
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\System.
Management.Automation\1.0....
Table of Contents | About PowerShell Plus 192 Sponsors | Resources | BBS Technologies
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\System.
Configuration.Install\2.0....
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\Microsoft.
PowerShell.Commands.Man...
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\Microsoft.
PowerShell.Security\1.0...
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\Microsoft.
PowerShell.Commands.Uti...
True v2.0 C:\Windows\assembly\GAC_MSIL\Microsoft.
PowerShell.ConsoleHost....
True v2.0.50727 C:\Windows\assembly\GAC_32\System.
Data\2.0.0.0__b77a5c561934e0...
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\System.
Xml\2.0.0.0__b77a5c561934e...
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\System.
DirectoryServices\2.0.0.0_...
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\System.
Management\2.0.0.0__b03f5f...
True v2.0 C:\Windows\assembly\GAC_MSIL\System.
Management.Automation.reso...
True v2.0.50727 C:\Windows\Microsoft.NET\Framework\
v2.0.50727\mscorlib.dll
True v2.0 C:\Windows\assembly\GAC_MSIL\Microsoft.
PowerShell.Security.res...
True v2.0 C:\Windows\assembly\GAC_MSIL\Microsoft.
PowerShell.Commands.Uti...
True v2.0.50727 C:\Windows\assembly\GAC_MSIL\System.
Configuration\2.0.0.0__b03...
You may see more assemblies than listed above. Any PowerShell
snap-in loads its own assemblies, and the
System.Reflection.Assembly type provides methods to manually
load additional assemblies from .NET DLL files or the global
assembly cache.
"inding &nteresting Classes LT!pesM
To find out which types are located in an assembly, use the 'et-(ported6ypes%& method provided by
an assembly. Since most assemblies contain way too many types you could search for a specific
keyword. This code list all types that include the search word "environment":
$searchtext = "*Environment*"
FAppDomainG::CurrentDomain.GetAssemblies$& |
ForEach-Object 9 $_.GetExportedTypes$& : |
Where-Object 9 $_ -like $searchtext : |
ForEach-Object 9 $_.FullName :
System.EnvironmentVariableTarget
Table of Contents | About PowerShell Plus 193 Sponsors | Resources | BBS Technologies
System.Environment
System.Environment+SpecialFolder
System.Runtime.InteropServices.RuntimeEnvironment
System.Security.Permissions.EnvironmentPermissionAccess
System.Security.Permissions.EnvironmentPermission
System.Security.Permissions.EnvironmentPermissionAttribute
System.ComponentModel.Design.Data.IDataEnvironment
Microsoft.PowerShell.Commands.EnvironmentProvider
System.Web.Configuration.HostingEnvironmentSection
System.Web.Hosting.HostingEnvironment
One of the types that show up is the type System.Environment. The Syste!.-n$iron!ent type can
do very useful things. Let's list its static members:
FSystem.EnvironmentG | Get-Member -static
TypeName: System.Environment
Name MemberType Definition
---- ---------- ----------
Exit Method static System.Void
Exit(Int32 exitCode)
ExpandEnvironmentVariables Method static System.String
ExpandEnvironmentVariables...
FailFast Method static System.Void
FailFast(String message)
GetCommandLineArgs Method static System.String[]
GetCommandLineArgs()
GetEnvironmentVariable Method static System.String
GetEnvironmentVariable(...
GetEnvironmentVariables Method static System.Collections.
IDictionary GetEnvironmentV...
GetFolderPath Method static System.String
GetFolderPath(SpecialFolder...
GetLogicalDrives Method static System.String[]
GetLogicalDrives()
SetEnvironmentVariable Method static System.Void
SetEnvironmentVariable(...
CommandLine Property static System.String
CommandLine {get;}
CurrentDirectory Property static System.String
CurrentDirectory {get;set;}
ExitCode Property static System.Int32 ExitCode
{get;set;}
HasShutdownStarted Property static System.Boolean
HasShutdownStarted {get;}
MachineName Property static System.String
MachineName {get;}
NewLine Property static System.String
NewLine {get;}
OSVersion Property static System.
OperatingSystem OSVersion
{get;}
Table of Contents | About PowerShell Plus 194 Sponsors | Resources | BBS Technologies
ProcessorCount Property static System.Int32
ProcessorCount {get;}
StackTrace Property static System.String
StackTrace {get;}
SystemDirectory Property static System.String
SystemDirectory {get;}
TickCount Property static System.Int32
TickCount {get;}
UserDomainName Property static System.String
UserDomainName {get;}
UserInteractive Property static System.Boolean
UserInteractive {get;}
UserName Property static System.String
UserName {get;}
Version Property static System.Version
Version {get;}
WorkingSet Property static System.Int64
WorkingSet {get;}
For example, the static methods of the Syste!.-n$iron!ent class will show you which user has
executed the script on which machine:
Fsystem.EnvironmentG::UserDomainName +
"\" + FSystem.EnvironmentG::UserName +
" on " + FSystem.EnvironmentG::MachineName
Idera\Tobias Weltner on PC12
Using 'et/olderPath%&, the class will also reveal the paths to all important Windows folders. To find
out the proper values for an argument, specify an invalid argument, and the error message will list
all valid argument values:
FSystem.EnvironmentG::GetFolderPath$"HH"&
Cannot convert argument "0", with value: "HH", for
"GetFolderPath" to type "System.Environment+SpecialFolder":
"Cannot convert value "HH" to type "System.Environment+
SpecialFolder" due to invalid enumeration values. Specify
one of the following enumeration values and try again.
The possible enumeration values are "Desktop, Programs,
Personal, MyDocuments, Favorites, Startup, Recent, SendTo,
StartMenu, MyMusic, DesktopDirectory, MyComputer,Templates,
ApplicationData, LocalApplicationData, InternetCache,
Cookies, History, CommonApplicationData, System,
ProgramFiles, MyPictures, CommonProgramFiles"."
So, if you'd like to know where the picture folder is on your computer, use MyPictures.
FSystem.EnvironmentG::GetFolderPath$"MyPictures"&
C:\Users\Tobias Weltner\Pictures
Table of Contents | About PowerShell Plus 195 Sponsors | Resources | BBS Technologies
Looking 0or 'ethods
Next, let's search for some interesting methods. Here's a way for you to find the 'et/olderPath%&
method of the previous example:
$searchtext = "*getfolder*"
FAppDomainG::CurrentDomain.GetAssemblies$& |
ForEach-Object 9 $_.GetExportedTypes$& : |
ForEach-Object 9 $_.getmembers$& : |
Where-Object 9 $_.isStatic: |
Where-Object 9 $_ -like $searchtext : |
ForEach-Object 9 "[{0}]::{1} --> {2}" -f `
$_.declaringtype, $_.toString$&.SubString$ `
$_.toString$&.IndexOf$" "&+!&, $_.ReturnType :
[System.Environment]::GetFolderPath(SpecialFolder)
--> System.String
The search can easily take a few minutes because PowerShell examines every single method in
every type exposed by every loaded assembly.
Creating 7ew *bJects
You have seen that many .NET types contain useful static methods. In addition, you can create new
objects (instances) that are derived from a specific type. To create new instances, you can either
convert an existing object to a new type, or you can create a new instance using 5ew-,b>ect. In
addition, you may be able to call some cmdlet or method that happens to return the object type you
are after:
$datetime = FSystem.DateTimeG '1.1.2000'
$datetime.GetType$&.Fullname
System.DateTime
$datetime = New-Object System.DateTime
$datetime.GetType$&.Fullname
System.DateTime
$datetime = Get-Date
$datetime.GetType$&.Fullname
System.DateTime
$datetime = FSystem.DateTimeG::Parse$'1.1.2000'&
$datetime.GetType$&.Fullname
System.DateTime
Table of Contents | About PowerShell Plus 196 Sponsors | Resources | BBS Technologies
Creating 7ew *bJects with 7ew(*bJect
You can create a .NET object with 5ew-,b>ect, which gives you full access to all type "constructors."
These are invisible methods that create the new object. To create a new instance of a type, the type
needs to have at least one constructor. If it has none, you cannot create instances of this type.
The DateTime type has one constructor that takes no argument. If you create a new instance of a
DateTime object, you get back a date set to the very first date a DateTime type can represent which
happens to be January 1, 0001:
New-Object System.DateTime
Monday, January 01, 0001 12:00:00 AM
To create a specific date, you would use a different constructor. There is one that takes three
numbers for year, month, and day:
New-Object System.DateTime$2""",%,!&
Monday, May 01, 2000 12:00:00 AM
If you simply add a number, yet another constructor is used which interprets the number as ticks,
the smallest time unit a computer can process:
New-Object System.DateTime$%6I6I*6*6*IA"I"AAA&
Monday, February 07, 1803 7:54:38 AM
8sing Constructors
When you create a new object using 5ew-,b>ect, you can submit additional arguments by adding
argument values as a comma separated list enclosed in parentheses. 5ew-,b>ect in fact is calling a
method called ctor which is the type constructor. Like any other method, it can support different
argument signatures.
Let's check out how you can discover the different constructors a type supports. The next line
creates a new instance of a System.String and uses a constructor that accepts a character and a
number:
New-Object ;<6tem.String$".", !""&
..................................................
..................................................
To list the available constructors for a type, use the GetConstructors() method available in each
type. For example, you could find out which constructors are offered by the Syste!.Strin type to
produce Syste!.Strin objects:
F;<6tem.StringG.GetConstructors$& |
Table of Contents | About PowerShell Plus 197 Sponsors | Resources | BBS Technologies
ForEach-Object 9 $_.toString$& :
Void .ctor(Char*)
Void .ctor(Char*, Int32, Int32)
Void .ctor(SByte*)
Void .ctor(SByte*, Int32, Int32)
Void .ctor(SByte*, Int32, Int32,
System.Text.Encoding)
Void .ctor(Char[], Int32, Int32)
Void .ctor(Char[])
Void .ctorH'har, .nt4*I
In fact, there are eight different signatures to create a new object of the Syste!.Strin type. You
just used the last variant: the first argument is the character, and the second a number that
specifies how often the character is to be repeated. PowerShell itself uses the next to last
constructor so if you specify text in quotation marks, it will interpret text in quotation marks as a
field with nothing but characters ("harCD).
7ew *bJects b! Conversion
Objects can often be created without 5ew-,b>ect by using type casting instead. You've already seen
how it's done for variables in Chapter 3:
# PowerShell normally wraps text as a System.String:
$date = "November 1, 2007"
$date.GetType$&.FullName
System.String
$date
November 1, 2007
# Use strong typing to set the object type of $date:
FSystem.DateTimeG$date = "November 1, 2007"
$date.GetType$&.FullName
System.DateTime
$date
Thursday, November 1, 2007 00:00:00
So, if you enclose the desired .NET type in square brackets and put it in front of a variable name,
PowerShell will require you to use precisely the specified object type for this variable. If you assign a
value to the variable, PowerShell automatically converts it to that type. That process is sometimes
called "implicit type conversion." Explicit type conversion works a little different. Here, the desired
type is put in square brackets again but placed on the right side of the assignment operator:
$value = FDateTimeG"November 1, 2007"
Table of Contents | About PowerShell Plus 198 Sponsors | Resources | BBS Technologies
$value
Thursday, November 1, 2007 00:00:00
PowerShell would first convert the text into a date because of the type specification and then assign
it to the variable =$alue, which itself remains a regular variable without type specification. Because
$value is not limited to DateTime types, you can assign other data types to the variable later on.
$value = "McGuffin"
Using the type casting, you can also create entirely new objects without 5ew-,b>ect. First, create an
object using 5ew-,b>ect:
New-Object system.diagnostics.eventlog$"System"&
Max(K) Retain OverflowAction Entries Name
------ ------ -------------- ------- ----
20,480 0 OverwriteAsNeeded 64,230 System
You could have accomplished the same thing without 5ew-,b>ect:
FSystem.Diagnostics.EventLogG"System"
Max(K) Retain OverflowAction Entries Name
------ ------ -------------- ------- ----
20,480 0 OverwriteAsNeeded 64,230 System
In the second example, the string System was converted into the Syste!..ianostics.-$entlo type:
The result is an -$entLo object representing the Syste! event log.
So, when should you use 5ew-,b>ect and when type conversion? It is largely a matter of taste, but
whenever a type has more than one constructor and you want to select the constructor, use 5ew-
,b>ect and specify the arguments for the constructor of your choice. Type conversion automatically
chooses one constructor, and you have no control over which constructor is picked.
# Using New-Object, you can select the
# constructor you wish of the type yourself:
New-Object ;<6tem.String$".", !""&
..................................................
..................................................
# When casting types, PowerShell selects the
# constructor automatically. For the System.String
# type, a constructor will be chosen that requires
# no arguments. Your arguments will then be
# interpreted as a /o%er5hell subexpression in
# which an array will be created. PowerShell will
# change this array into a System.String type.
# PowerShell changes arrays into text by separating
# elements from each other with whitespace:
Table of Contents | About PowerShell Plus 199 Sponsors | Resources | BBS Technologies
F6<6tem.stringG$".",!""&
. 100
# If your arguments are not in round brackets,
# they will be interpreted as an array and the first
# array element cast in the System.String type:
F6<6tem.stringG".", !""
.
100
Type conversion can also include type arrays (identified by "[]")
and can be a multi-step process where you convert from one type
over another type to a final type. This is how you would convert
string text into a character array:
FcharFGG"Hello!"
H
e
l
l
o
!
You could then convert each character into integers to get the character
codes:
FIntFGGFCharFGG"Hello World!"
72
97
108
108
111
32
87
101
108
116
33
Conversely, you could make a numeric list out of a numeric array, and turn
that into a string:
FstringGFcharFGG$6%..A"&
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
Table of Contents | About PowerShell Plus 200 Sponsors | Resources | BBS Technologies
$OFS = ","
FstringGFcharFGG$6%..A"&
A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z
Just remember: if arrays are converted into a string, PowerShell uses the
separator in the =,/S automatic variable as a separator between the array
elements.
Loading Additional Assemblies: &mproved &nternet )ownload
To get access to even more functionality, you can load additional assemblies with more types and
members. If you have ever written VBScript scripts, you may want to get back some of your beloved
VisualBasic methods such as MsgBox() or InputBox(). Simply load the Microsoft.VisualBasic
assembly, which is located in the global assembly cache:
# Load required assembly:
FvoidGFreflection.assemblyG::`
Load7ithartialName$"Microsoft.VisualBasic"&
Once you did that, you have access to a whole bunch of new types:
FMicrosoft.VisualBasic.InteractionG | Get-Member -static
TypeName: Microsoft.VisualBasic.Interaction
Name MemberType Definition
---- ---------- ----------
AppActivate Method static System.Void
AppActivate(Int32 Proces...
Beep Method static System.Void
Beep()
CallByName Method static System.Object
CallByName(Object Obje...
Choose Method static System.Object
Choose(Double Index, P...
Command Method static System.String
Command()
CreateObject Method static System.Object
CreateObject(String Pr...
DeleteSetting Method static System.Void
DeleteSetting(String App...
Environ Method static System.String
Environ(Int32 Expressi...
Equals Method static System.Boolean
Equals(Object objA, O...
GetAllSettings Method static System.String[,]
GetAllSettings(Stri...
Table of Contents | About PowerShell Plus 201 Sponsors | Resources | BBS Technologies
GetObject Method static System.Object
GetObject(String PathN...
GetSetting Method static System.String
GetSetting(String AppN...
IIf Method static System.Object
IIf(Boolean Expression...
InputBox Method static System.String
InputBox(String Prompt...
MsgBox Method static Microsoft.VisualBasic.
MsgBoxResult M...
Partition Method static System.String
Partition(Int64 Number...
ReferenceEquals Method static System.Boolean
ReferenceEquals(Objec...
SaveSetting Method static System.Void
SaveSetting(String AppNa...
Shell Method static System.Int32
Shell(String PathName, ...
Switch Method static System.Object
Switch(Params Object[]...
Fmicrosoft.VisualBasic.InteractionG::`
1np8t)o-$"Enter Name", "Name", "$env:username"&
Tobias
Or, you could use a much-improved download method, which shows a progress bar while
downloading files from the Internet:
# Reload required assembly:
FvoidGFreflection.assemblyG::`
Load7ithartialName$"Microsoft.VisualBasic"&
# Download address of a file:
$address = "http://powershell.com/cs/" +
"media/p/467/download.aspx"
# This is where the file should be saved:
$target = "$home\chart_drive_space.V2.ps1"
# Download will be carried out:
$object = New-Object `
Microsoft.VisualBasic.Devices.Network
$object.DownloadFile$
$address, $target, "", "",
$true, %"", $true, "DoNothing"&
8sing C*' *bJects
In addition to .NET, PowerShell can also load and access COM objects which work similar to .NET
types and objects but use an older technology.
Table of Contents | About PowerShell Plus 202 Sponsors | Resources | BBS Technologies
3hich C*' *bJects Are AvailableN
COM objects each have a unique name known as Pro4. or Prora!!atic 4denti7ier, which is stored
in the registry. So, if you want to look up COM objects available on your computer, visit the registry:
(ir @0G1;3@O::HKEY_CLASSES_ROOT\CL;1( `
-include @CG1( -recurse |
foreach 9$_.GetValue$""&:
+ow )o Gou 8se C*' *bJectsN
Once you know the Pro4. of a COM component, use 5ew-,b>ect to put it to work in PowerShell.
Just specify the additional parameter -co!,b>ect:
$object = New-Object -comObject WScript.Shell
You'll get an object which behaves very similar to .NET objects. It will contain properties with data
and methods that you can execute. And, as always, 'et-Me!ber finds all object members for you.
Let's look at its methods:
# Make the methods of the COM objects visible:
$object | Get-Member -memberType *method
TypeName: System.__ComObject#{41904400-be18-
11d3-a28b-00104bd35090}
Name MemberType Definition
---- ---------- ----------
AppActivate Method bool AppActivate (Variant, Variant)
CreateShortcut Method IDispatch CreateShortcut (string)
Exec Method IWshExec Exec (string)
ExpandEnviron Method string ExpandEnvironmentStrings
mentStrings (string)
LogEvent Method bool LogEvent (Variant, string,
string)
Popup Method int Popup (string, Variant,
Variant, Variant)
RegDelete Method void RegDelete (string)
RegRead Method Variant RegRead (string)
RegWrite Method void RegWrite (string, Variant,
Variant)
Run Method int Run (string, Variant, Variant)
SendKeys Method void SendKeys (string, Variant)
The information required to understand how to use a method can be inadequate. Only the expected
object types are given, but not why the arguments exist. If you want to know more about a COM
command, the Internet can help you. Go to a search site of your choice and enter two keywords: the
Pro4. of the COM components (in this case, it will be WScript.Shell) and the name of the method
that you want to use.
Table of Contents | About PowerShell Plus 203 Sponsors | Resources | BBS Technologies
Some of the commonly used COM objects are WScript.Shell, WScript.Network,
Scripting.FileSystemObject, InternetExplorer.Application, Word.Application, and Shell.Application.
Let's create a shortcut to powershell.exe using WScript.Shell Com object and its method
CreateShorcut():
# Create an object:
$wshell = New-Object -comObject WScript.Shell
# Assign a path to Desktop to the variable $path
$path = Fsystem.EnvironmentG::GetFolderPath$'Desktop'&
# Create a link object
$link = $wshell.CreateShortcut$"$path\PowerShell.lnk"&
# $link is an object and has the properties and methods
$link | Get-Member
TypeName: System.__ComObject#{f935dc23-1cf0-11d0-adb9-00c04fd58a0b}
Name MemberType Definition
---- ---------- ----------
Load Method void Load (string)
Save Method void Save ()
Arguments Property string Arguments () {get} {set}
Description Property string Description () {get} {set}
FullName Property string FullName () {get}
Hotkey Property string Hotkey () {get} {set}
IconLocation Property string IconLocation () {get} {set}
RelativePath Property {get} {set}
TargetPath Property string TargetPath () {get} {set}
WindowStyle Property int WindowStyle () {get} {set}
WorkingDirectory Property string WorkingDirectory () {get} {set}
# We can populate some of the properties
$link.TargetPath = 'powershell.exe'
$link.Description = 'Launch Windows PowerShell console'
$link.WorkingDirectory = $profile
$link.IconLocation = 'powershell.exe'
# And save the changes using Save() method
$link.Save$&
Summar!
Everything in PowerShell is represented by objects that have exactly two aspects: properties and
methods, which both form the members of the object. While properties store data, methods are
executable commands.
Objects are the result of all PowerShell commands and are not converted to readable text until you
output the objects to the console. However if you save a command's result in a variable, you will get
a handle on the original objects and be able to evaluate their properties or call their commands. If
you would like to see all of an object's properties, then pass the object to /or!at-List and type an
asterisk after it. In this way, all-and not only the most important-properties will be output as text.
Table of Contents | About PowerShell Plus 204 Sponsors | Resources | BBS Technologies
The 'et-Me!ber cmdlet retrieves even more data, enabling you to output detailed information on
the properties and methods of any object.
All the objects with which you work in PowerShell originate from .NET framework, on which
PowerShell is layered. Aside from the objects that PowerShell commands provide you as results, you
can also invoke objects directly from the .NET framework and gain access to a powerful arsenal of
new commands. Along with the dynamic methods furnished by objects, there are also static
methods, which are provided directly by the class from which objects are also derived.
If you cannot perform a task either with the cmdlets, regular console commands, or methods of
the .NET framework, you can resort to the unmanaged world outside the .NET framework. Either
directly access the low-level API functions, the foundation of the .NET framework, or use COM
components.
Table of Contents | About PowerShell Plus 205 Sponsors | Resources | BBS Technologies
CHAPTER 7.
Conditions
Conditions
You'll need a condition first to compose intelligent PowerShell code capable of making decisions.
That's why you'll learn in the first part of this Chapter how to formulate questions as conditions.
In the second part, you'll employ conditions to execute PowerShell instructions only if a particular
condition is actually met.
Topics Covered:
Formulating Conditions
Table 7.1: Comparison operators
Carrying Out a Comparison
"Reversing" Comparisons
Combining Comparisons
Table 7.2: Logical operators
Comparisons with Arrays and Collections
Verifying Whether an Array Contains a Particular Element
Where-Object
Filtering Results in the Pipeline
Formulating a Condition
Using Alias
If-ElseIf-Else
Switch
Testing Range of Values
No Applicable Condition
Several Applicable Conditions
Using String Comparisons
Case Sensitivity
Wildcard Characters
Regular Expressions
Processing Several Values Simultaneously
Summary
"ormulating Conditions
A condition is nothing more than a question that can be answered clearly in the positive (true) or in
the negative (7alse). Nearly all questions are phrased with the help of comparisons. The following
PowerShell comparison operators allow you to compare values:
Operator Conventional Description Example Result
-eq, -ceq, -ieq = equals 10 -eq 15 $false
Table of Contents | About PowerShell Plus 206 Sponsors | Resources | BBS Technologies
-ne, -cne, -ine <> not equal 10 -ne 15 $true
-gt, -cgt, -igt > greater than 10 -gt 15 $false
-ge, -cge, -ige >=
greater than or
equal to
10 -ge 15 $false
-lt, -clt, -ilt < less than 10 -lt 15 $true
-le, -cle, -ile <=
less than or equal
to
10 -le 15 $true
-contains,
-ccontains,
-icontains
contains 1,2,3 -contains 1 $true
-notcontains,
-cnotcontains,
-inotcontains
does not contain
1,2,3 -notcontains
1
$false
Table 5$#: Comparison operators
PowerShell doesn't use the traditional comparison operators that
you may know from other programming languages. In particular,
the "=" operator is purely an assignment operator in PowerShell,
while ">" and "<" operators are used for redirection.
There are three variants of all comparison operators. The basic variant is case-insensitive when
making comparisons. If you'd like to explicitly specify whether case sensitivity should be taken into
account, use variants that begin with "c" (case-sensitive) or "i" (case-insensitive).
Carr!ing *ut a Comparison
You can carry out comparisons immediately and directly in the PowerShell console. First, enter a
value, then a comparison operator, and then the second value that you want to compare with the
first. As soon as you hit (enter), the comparison will be performed. The result should always be 6rue
(condition is correct) or /alse (condition is incorrect).
# -eq !"
Table of Contents | About PowerShell Plus 207 Sponsors | Resources | BBS Technologies
False
"secret" -ieq "SECRET"
True
As long as you compare only numbers or only strings, comparisons are very easy to grasp and
return exactly the result that you expect:
!2' -lt !2'.%
True
However, you can also compare different data types. These results are not always as logical as the
previous one:
!2 -eq "Hello"
False
!2 -eq "000012"
True
"12" -eq !2
True
"12" -eq "!2
True
"012" -eq "!2
False
!2' -lt !2'.#
True
!2' -lt "123.4"
False
!2' -lt "123.5"
True
Table of Contents | About PowerShell Plus 208 Sponsors | Resources | BBS Technologies
Would you have expected these results? Some comparisons return peculiar results. That's precisely
what happens when you compare di77erent data types, and the reason is that PowerShell actually
cannot compare different data types at all. PowerShell tries to convert the data types into a common
data type that can be compared. However, this automatic conversion doesn't always return the
result that you would intuitively expect, so you should avoid comparisons of differing data types.
<,eversing< Comparisons
A comparison always returns a result that is either true or 7alse, and you've seen that there are
complementary comparison operators for most comparisons: -e: and -ne (equal and not equal) or -
t and -lt (greater than and less than).
In addition, with the logical operator -not you have the option of "reversing" the result of a
comparison. It expects an expression on the right side that is either true or 7alse, and it turns this
around. Instead of -not, you may also use the abbreviated "!":
$a = !"
$a -gt %
True
-not $$a -gt %&
False
# Shorthand: instead of -not "!" can also be used:
!$$a -gt %&
False
Make generous use of parentheses if you're working with logical
operators like -not. Logical operators are always interested in the
result of a comparison, but not in the comparison itself. That's why
the comparison should always be in parentheses.
Combining Comparisons
Because every comparison returns either 6rue or /alse, you can combine several comparisons with
logical operators. The following conditional statement would evaluate to true only if both
comparisons evaluate to true:
$ $$age -ge !I& -and $$sex -eq "m"& &
Put separate comparisons in parentheses because you only want to link the results of these
comparisons, certainly not the comparisons themselves.
Table of Contents | About PowerShell Plus 209 Sponsors | Resources | BBS Technologies
Operator Description Left Value
Right
Value
Result
-and Both conditions must be met
True
False
False
True
False
True
False
True
False
False
False
True
-or
At least one of the two conditions
must be met
True
False
False
True
False
True
False
True
True
True
False
True
-xor
One or the other condition must be
met, but not both
True
False
False
True
True
False
True
False
False
False
True
True
-not Reverses the result
(not
applicable)
True
False
False
True
Table 5$%: Logical operators
Comparisons with Arra!s and Collections
Up to now, you've only used the comparison operators in Table 7.1 to compare single values. In
Chapter 4, you've already become familiar with arrays. The question is: how do comparison
operators react to arrays? To which element of an array is the comparison applied? The simple
answer: to all of them.
But the result is not a long list of 6rue and /alse. In this case, comparison operators return an array
in which precisely those elements of the initial array reappear in the matched comparison-
resembling a sort of filter. In the simplest case, use the comparison operator -e: (e:ual) to find all
elements in an array equal to the specified value:
!,2,',#,',2,! -eq '
3
3
Two elements having the value of 3 are in the initial array. Only these two elements were returned.
It works conversely, too: if you'd like to see only the elements of an array that don't match the
comparison value, use -ne (not e:ual) operator:
!,2,',#,',2,! -ne '
Table of Contents | About PowerShell Plus 210 Sponsors | Resources | BBS Technologies
1
2
4
2
1
Ieri0!ing 3hether an Arra! Contains a Particular Element
How can you find out whether an array contains a particular element? As you have seen, -eq
provides no answer to this question. That's why there are the comparison operators -contains and
-notcontains. They verify whether a certain value exists in an array.
# -eq returns only those elements matching the criterion:
!,2,' -e= %
# -contains answers the question of whether the sought element is included in the
array:
!,2,' -contains %
False
!,2,' -notcontains %
True
3here(*bJect
Let's now apply conditions in real life. The first area of application is the PowerShell pipeline, which
you became acquainted with in Chapter 5. In the pipeline, the results of a command are forwarded
directly to the next one, and the Where-,b>ect cmdlet works like a filter, allowing only those objects
through the pipeline that meet a certain condition. To make this work, specify your condition to
Where-,b>ect.
"iltering ,esults in the Pipeline
The cmdlet 'et-Process, will return all running processes. However, you are not likely to be
interested in all processes, but instead you want an answer to a specific problem. For instance, you
would like to find out currently running instances of the Notepad. First, get an initial overview of
which properties the processes contain by using 'et-Process. That's important, because you'll use
these properties afterwards as the basis for your condition. This is how you can find the available
properties:
Get-Process | Select-Object -first ! | Format-List *
__NounName : Process
Name : agrsmsvc
Handles : 36
Table of Contents | About PowerShell Plus 211 Sponsors | Resources | BBS Technologies
VM : 21884928
WS : 57344
PM : 716800
NPM : 1768
Path :
Company :
CPU :
FileVersion :
ProductVersion :
Description :
Product :
Id : 1316
PriorityClass :
HandleCount : 36
WorkingSet : 57344
PagedMemorySize : 716800
PrivateMemorySize : 716800
VirtualMemorySize : 21884928
TotalProcessorTime :
BasePriority : 8
ExitCode :
HasExited :
ExitTime :
Handle :
MachineName : .
MainWindowHandle : 0
MainWindowTitle :
MainModule :
MaxWorkingSet :
MinWorkingSet :
Modules :
NonpagedSystemMemorySize : 1768
NonpagedSystemMemorySize64 : 1768
PagedMemorySize64 : 716800
PagedSystemMemorySize : 24860
PagedSystemMemorySize64 : 24860
PeakPagedMemorySize : 716800
PeakPagedMemorySize64 : 716800
PeakWorkingSet : 2387968
PeakWorkingSet64 : 2387968
PeakVirtualMemorySize : 21884928
PeakVirtualMemorySize64 : 21884928
PriorityBoostEnabled :
PrivateMemorySize64 : 716800
PrivilegedProcessorTime :
ProcessName : agrsmsvc
ProcessorAffinity :
Responding : True
SessionId : 0
StartInfo : System.Diagnostics.ProcessStartInfo
StartTime :
SynchronizingObject :
Threads : {1964, 1000}
Table of Contents | About PowerShell Plus 212 Sponsors | Resources | BBS Technologies
UserProcessorTime :
VirtualMemorySize64 : 21884928
EnableRaisingEvents : False
StandardInput :
StandardOutput :
StandardError :
WorkingSet64 : 57344
Site :
Container :
"ormulating a Condition
The name of a process can be found in the 5a!e property. If you're just looking for the processes of
the Notepad, your condition should be na!e -e: QnotepadQ. Now, supply this condition to Where-
,b>ect:
Get-Process | Where-Object 9 $_.name -eq 'notepad' :
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
68 4 1636 8744 62 0,14 7732 notepad
68 4 1632 8764 62 0,05 7812 notepad
The pipeline now returns only those processes that meet your condition. If you're not currently
running the Notepad, nothing will be returned. If you take a closer look at Where-,b>ect, you'll see
that your condition is specified in braces after the cmdlet. The =3 variable contains the current
pipeline object.
6he ne(t one-liner would retrie$e all processes whose co!pany na!e beins with BMicroB and
output 7or each process its na!e@ description@ and co!pany na!e)
Get-Process | Where-Object 9 $_.company -like 'micro*' : |
Format-Table name, de6cription, compan<
Name Description Company
---- ----------- -------
conime Console IME Microsoft Corporation
dwm Desktopwindow-Manager Microsoft Corporation
ehmsas Media Center Media Statu... Microsoft Corporation
ehtray Media Center Tray Applet Microsoft Corporation
EXCEL Microsoft Office Excel Microsoft Corporation
explorer Windows-Explorer Microsoft Corporation
GrooveMonitor GrooveMonitor Utility Microsoft Corporation
ieuser Internet Explorer Microsoft Corporation
iexplore Internet Explorer Microsoft Corporation
msnmsgr Messenger Microsoft Corporation
notepad Editor Microsoft Corporation
notepad Editor Microsoft Corporation
sidebar Windows-Sidebar Microsoft Corporation
taskeng Task Scheduler Engine Microsoft Corporation
WINWORD Microsoft Office Word Microsoft Corporation
Table of Contents | About PowerShell Plus 213 Sponsors | Resources | BBS Technologies
wmpnscfg Windows Media Player Net... Microsoft Corporation
wpcumi Windows Parental Control... Microsoft Corporation
In Chapter 6 you learned that every single process in this list is
actually an object that not only has the properties that you just
made visible in the previous example, but also has methods. That
means you could go on to process the result of your condition
object by object and, in doing so, invoke methods for every object.
To do so, you need loops, which will be explained in more detail in the next
Chapter. However, for the time being, here's a little preview: the next line
ends all Notepad processes. Watch out: the processes will be ended
immediately and without request for confirmation. All data that you haven't
saved will be lost:
# Attention: all instances of Notepad will be terminated
# immediately and without further notification:
Get-Process | Where-Object 9 $_.name -eq 'notepad' : |
Foreach-Object 9 $_.Kill$& :
8sing Alias
Because you often need conditions in the pipeline, an alias exists for Where-,b>ect: "?". So, instead
of Where-,b>ect, you may also use "?'".
# The two following instructions return the same result:
# all running services
Get-Service | ForEach-Object 9$_.Status -eq 'Running' :
Get-Service | L 9$_.Status -eq 'Running' :
&0(Else&0(Else
Where-ob>ect works splendidly in the pipeline, but it is inappropriate if you want to make longer
code segments dependent on meeting a condition. Here, the 47..-lse47..-lse statement works much
better. In the simplest case, the statement looks like this:
If $condition& 9
# If the condition applies,
# this code will be executed
:
The condition must be enclosed in parentheses and follow the keyword 47. If the condition is met, the
code in the braces after it will be executed, otherwise not. Try it out:
If ($a -gt 10) { "$a is larger than 10" }
Table of Contents | About PowerShell Plus 214 Sponsors | Resources | BBS Technologies
It's likely, though, that you won't (yet) see a result. The condition was not met, and so the code in
the braces wasn't executed. To get an answer, make sure that the condition is met:
$a = !!
If $$a -gt !"& 9 "$a is larger than 10" :
11 is larger than 10
Now the comparison is correct, and the 47 statement ensures that the code in the braces returns a
result. As it is, that clearly shows that the simplest 47 statement usually doesn't suffice in itself,
because you would like to always get a result, even when the condition isn't met. To accomplish
that, expand the 47 statement with -lse:
If $$a -gt !"&
9
"$a is larger than 10"
:
Else
9
"$a is less than or equal to 10"
:
Now the code in the braces after 47 is executed if the condition is met; if the preceding condition isn't
true, the code in the braces after -lse is e(ecuted. If you have several conditions you may insert as
many -lse47 blocks between 47 and -lse as you like:
If $$a -gt !"&
9
"$a is larger than 10"
:
ElseIf $$a -eq !"&
9
"$a is exactly 10"
:
Else
9
"$a is less than 10"
:
The 47 statement here always executes the code in the braces after the condition that is met. The
code after -lse will be executed when none of the preceding conditions are true. What happens if
several conditions are true? Then the code after the first applicable condition will be executed and all
other applicable conditions will be ignored.
If $$a -gt !"&
9
"$a is larger than 10"
:
ElseIf $$a -eq !"&
9
"$a is exactly 10"
:
Table of Contents | About PowerShell Plus 215 Sponsors | Resources | BBS Technologies
ElseIf $$a -ge !"&
9
"$a is larger than or equal to 10"
:
Else
9
"$a is smaller than 10"
:
The fact is that the 47 statement doesn't care at all about the condition that
you state. All that the 47 statement evaluates is =true or =7alse. If condition
evaluates =true, the code in the braces after it will be executed, otherwise
not. Conditions are only a way to return one of the requested values =true
or =7alse. But the value could come from another function or from a
variable:
# Returns True from 14:00 on, otherwise False:
Function i6.2ternoon 9 $Get-Date&.Hour -gt !' :
i6.2ternoon
True
# Result of the function determines which code the If statement
executes:
If $i6.2ternoon& 9 "Time for break!" : Else 9 "It's still early." :
Time for break!
The example shows that the condition after 47 must always be in parentheses, but it
can also come from any source as long as it is =true or =7alse. In addition, you may
also write the 47 statement in a single line. If you'd like to execute more than one
command in the braces without having to use new lines, separate the commands with
a semicolon ";".
Switch
If you'd like to test a value against many comparison values, the 47 statement could quickly become
confusing. The Switch statement is much clearer and quicker:
# Test a value against several comparison values (with If statement):
:#alue = !
If $$value -eq 1&
9
" Number 1"
:
ElseIf $$value -eq 2&
9
Table of Contents | About PowerShell Plus 216 Sponsors | Resources | BBS Technologies
" Number 2"
:
ElseIf $$value -eq 3&
9
" Number 3"
:
Number 1
# Test a value against several comparison values (with Switch statement):
$value = !
Switch $:#alue&
9
1 9 "Number 1" :
2 9 "Number 2" :
3 9 "Number 3" :
:
Number 1
This is how to use the Switch state!ent: the value to switch on is in the parentheses after the
Switch keyword. That value is matched with each of the conditions case by case. If a match is found,
the action associated with that condition is performed. Default comparison operator is the -e:
operator to verify equality.
Testing ,ange o0 Ialues
The default comparison operator is -e: operator, but you could also compare a value with your own
conditions. Formulate your own condition and put it in braces. Condition must result in either true or
7alse:
$value = I
Switch $$value&
9
# Instead of a standard value, a code block is used
# that results in True for numbers smaller than 5:
{:77-le 5} 9 "Number from 1 to 5" :
# A value is used here; Switch checks whether this
# value matches $value:
6 9 "Number 6" :
# Complex conditions are allowed as they are here,
# where -and is used to combine two comparisons:
{((:77-gt 6) -and (:77-le 10))} 9 "Number from 7 to 10" :
:
Number from 7 to 10
The code block I=3 -le RJ includes all numbers less than or equal to 5.
Table of Contents | About PowerShell Plus 217 Sponsors | Resources | BBS Technologies
The code block I%%=3 -t S& -and %=3 -le 10&&J combines two conditions and results in true if
the number is either larger than 6 or less than-equal to 10. Consequently, you may combine
any PowerShell statements in the code block and also use the logical operators listed in Table
7.2.
Here, you used the initial value stored in =3 for your conditions, but because =3 is generally
available anywhere in the Switch block, you could just as well have put it to work in the result code:
$value = I
Switch $$value&
9
# The initial value (here it is in $value)
# is available in the variable $_:
9$_ -le %: 9 "$_ is a number from 1 to 5" :
6 9 "Number 6" :
9$$$_ -gt 6& -and $$_ -le !"&&:
9 "$_ is a number from 7 to 10" :
:
8 is a number from 7 to 10
7o Applicable Condition
In a similar manner as an 47 statement, the Switch statement executes code only if at least one of
the specified conditions is met. The keyword, which for the 47 statement is called -lse, is called
de7ault for Switch statement. When no other condition matches, the default clause is run.
$value = %"
Switch $$value&
9
9$_ -le %: 9 "$_is a number from 1 to 5" :
6 9 "Number 6" :
9$$$_ -gt 6& -and $$_ -le !"&&:
9 "$_ is a number from 7 to 10" :
# The code after the next statement will be
# executed if no other condition has been met:
default 9"$_ is a number outside the range from 1 to 10" :
:
50 is a number outside the range from 1 to 10
Several Applicable Conditions
If more than one condition applies, then Switch will behave differently than 47. For 47, only the first
applicable condition was executed. For Switch, all applicable conditions are executed:
$value = %"
Switch $$value&
9
Table of Contents | About PowerShell Plus 218 Sponsors | Resources | BBS Technologies
%" 9 "the number 50" :
9$_ -gt !": 9"larger than 10":
9$_ -is FintG: 9"Integer number":
:
The Number 50
Larger than 10
Integer number
Consequently, all applicable conditions ensure that the following code is executed, and so in some
circumstances you may get more than one result.
Try out that example, but assign 50.0 to =$alue. In this case, you'll
get just two results instead of three. Do you have any idea why?
That's right: the third condition is no longer fulfilled because the
number in =$alue is no longer an integer number. The other two
conditions, however, remain fulfilled.
If you'd like to receive only one result, while consequently making sure that only the first applicable
condition is performed, then append the break statement to the code.
$value = %"
Switch $$value&
9
%" 9 "the number 50"B break :
9$_ -gt !": 9"larger than 10"B break:
9$_ -is FintG: 9"Integer number"B break:
:
The number 50
In fact, now you get only the first applicable result. The keyword break indicates that no more
processing will occur and the Switch statement will exit.
8sing String Comparisons
The previous examples have always compared numbers. You could also naturally compare strings
since you now know that behind the scenes Switch uses only the normal -e: comparison operator
and that there string comparisons are also permitted. The following code could be the basic structure
of a command evaluation. A different action will be performed, depending on the specified
command:
$action = "sAVe"
Switch $$action&
9
"save" 9 "I save..." :
"open" 9 "I open..." :
Table of Contents | About PowerShell Plus 219 Sponsors | Resources | BBS Technologies
"print" 9 "I print..." :
(e2a8lt 9 "Unknown command" :
:
I save...
Case Sensitivit!
Since the -e: comparison operator doesn't distinguish between lower and upper case, case
sensitivity doesn't play any role in comparisons. If you want to distinguish between them, then use
the -case option. Working behind the scene, it will replace the -e: comparison operator with -ce:,
after which case sensitivity will suddenly become crucial:
$action = "sAVe"
Switch -case $$action&
9
"save" 9 "I save..." :
"open" 9 "I open..." :
"print" 9 "I print..." :
(e2a8lt 9 "Unknown command" :
:
Unknown command
3ildcard Characters
In fact, you can also exchange a standard comparison operator for -like and -!atch operators and
then carry out wildcard comparisons. Using the -wildcard option, activate the -like operator, which is
conversant, among others, with the "*" wildcard character:
$text = "IP address: 10.10.10.10"
Switch -wildcard $$text&
9
"IP*" 9 "The text begins with IP: $_" :
"*.*.*.*" 9 "The text contains an IP " +
"address string pattern: $_" :
"*dress*" 9 "The text contains the string " +
"'dress' in arbitrary locations: $_" :
:
The text begins with IP: IP address: 10.10.10.10
The text contains an IP address string pattern:
IP address: 10.10.10.10
The text contains the string 'dress' in arbitrary
locations: IP address: 10.10.10.10
Table of Contents | About PowerShell Plus 220 Sponsors | Resources | BBS Technologies
,egular Expressions
Simple wildcard characters can't always be used for recognizing patterns. Regular expressions are
much more efficient. But they assume much more basic knowledge, a reason for you to now take a
peek ahead at Chapter 13, which discusses regular expression in greater detail.
With the -ree( option, you can ensure that Switch uses the -!atch comparison operator instead of
-e:, and thus employs regular expressions. Using regular expressions, you can identify a pattern
much more precisely than by using simple wildcard characters. But that's not all: as was the case
with the -!atch operator, you will usually get back the text that matches the pattern in the
=!atches variable. This way, you could even parse information out of the text:
$text = "IP address: 10.10.10.10"
Switch -regex $$text&
9
"^IP" 9 "The text begins with IP: " +
"$($matches[0])" :
"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" 9
"The text contains an IP address " +
"string pattern: $($matches[0])" :
"\b.*?dress.*?\b" 9 " The text " +
"contains the string 'dress' in " +
"arbitrary locations: $($matches[0])" :
:
The text begins with IP: IP
The text contains an IP address string
pattern: 10.10.10.10
The text contains the string 'dress' in
arbitrary locations: IP address
The result of the -!atch comparison with the regular expression is
returned in =!atches, a hash table with each result, because
regular expressions can, depending on their form, return several
results. In the example, only the first result should interest you,
the one you got by using =!atchesC0D. To ensure that this result
appears in the output text, the entire expression is embedded in =%...&.
Processing Several Ialues Simultaneousl!
Until now, you have always passed to Switch just one value for evaluation. But Switch can also
process several values at the same time. To do so, pass to Switch the values in an array or a
collection. In the following example, Switch is passed an array containing five elements. Switch
automatically takes all the elements one at a time from the array and compares each of them, one
by one:
$array = !..%
Table of Contents | About PowerShell Plus 221 Sponsors | Resources | BBS Technologies
Switch $$array&
9
9$_ % 2: 9 "$_ is odd.":
(e2a8lt 9 "$_ is even.":
:
1 is odd.
2 is even.
3 is odd.
4 is even.
5 is odd.
There you have it: Switch accepts not only single values but also entire arrays and collections. As
such, Switch would actually be an ideal candidate for evaluating results on the PowerShell pipeline
because the pipeline character ("|") is used to forward results as arrays or collections from one
command to the next.
The next line queries 'et-Process for all running processes and pipes the result to a script block (T
I...J). In the script block, Switch evaluates the result of the pipeline, which is available in =input. If
the WS property of a process is larger than one megabyte, this process is output Switch will filter all
the processes whose WS property is less than or equal to one megabyte:
Get-Process | & 9 Switch$$input& 9 9$_.WS -gt !+): 9 $_ :::
However, this line is extremely hard to read and seems complicated. By using Where-,b>ect, you
can formulate the condition in a much clearer way:
Get-Process | Where-Object 9 $_.WS -gt !+) :
This variant is also quicker because Switch had to wait until the pipeline had collected the entire
results of the preceding command in =input. In Where-,b>ect, it processes the results of the
preceding command precisely when the results are ready. This difference is especially striking for
elaborate commands:
# Switch returns all files beginning with "a":
(ir | & 9 Switch$$input& 9
9$_.name.StartsWith$"a"&: 9 $_ : ::
# But it doesn't do so until Dir has retrieved
# all data, and that can take a long time:
(ir -Recurse | & 9 Switch$$input& 9
9$_.name.StartsWith$"a"&: 9 $_ : ::
# Where-Object processes the incoming results
# immediately:
(ir -recurse | Where-Object 9
$_.name.StartsWith$"a"& :
# The alias of Where-Object ("?") works
# exactly the same way:
(ir -recurse | L 9 $_.name.StartsWith$"a"& :
Table of Contents | About PowerShell Plus 222 Sponsors | Resources | BBS Technologies
Summar!
Intelligent decisions are based on conditions, which in the simplest form can be reduced to plain 2es
or 5o answers. Using the comparison operators listed in Table 7.1, you can formulate such
conditions and can even combine these with the logical operators listed in Table 7.2 to form complex
queries.
The simple Yes/No answers of your conditions determine whether particular PowerShell instructions
should be carried out or not. In the simplest form, you can use the Where-Object cmdlet in the
pipeline. It functions there like a filter, allowing only those results through the pipeline that
correspond to your condition.
If you would like more control, or would like to execute larger code segments independently of
conditions, use the 47 statement, which evaluates as many different conditions as you wish and,
depending on the result, executes the allocated code. This is the typical "If-Then" scenario: i7 certain
conditions are met, then certain code segments will be executed.
An alternative to the 47 statement is the Switch statement: using it, you can compare a fixed initial
value with various possibilities. Switch is the right choice when you want to check a particular
variable against many different possible values.
Table of Contents | About PowerShell Plus 223 Sponsors | Resources | BBS Technologies
CHAPTER 8.
#oops
#oops
Loops are a good example that iterations do not have to be boring. They repeat particular
PowerShell statements with the pipeline being one of the areas where you can benefit from loops.
Most PowerShell commands wrap their results in arrays, and you'll need a loop when you want to
examine single elements in an array more closely.
Topics Covered:
ForEach-Object
Evaluating Pipeline Objects Separately
Integrating Conditions
Invoking Methods
Foreach
Do and While
Continuation and Abort Conditions
Using Variables as Continuation Criteria
Endless Loops without Continuation Criteria
For
For Loops: Just Special Types of the While Loop
Unusual Uses for the For Loop
Switch
Processing File Contents Line by Line
Exiting Loops Early
Continue: Skipping Loop Cycles
Nested Loops and Labels
Summary
"orEach(*bJect
The PowerShell pipeline works like an assembly line. Each command is tied to the next and hands
over its result to the following command, pretty much like assembly line robots. So, the results from
the initial command will be processed by all other commands in real time. If you'd like to look more
closely at these objects, you'll need the /or-ach-,b>ect cmdlet. It executes the code that you specify
after it for every object that is guided through the pipeline. This is one of the most important ways
to acquire native PowerShell objects. At the same time, it's the simplest form of a loop.
Evaluating Pipeline *bJects Separatel!
If you use 'et-W!i,b>ect to retrieve all information about all running services, 'et-W!i,b>ect will
acquire the services as objects and direct them through the pipeline. Normally, PowerShell converts
these objects into text when they reach the end of the pipeline; at most, you could format the
output by using the formatting cmdlets described in Chapter 5:
Get-WmiObject 7in'2E;er4ice |
Table of Contents | About PowerShell Plus 224 Sponsors | Resources | BBS Technologies
Format-Table Name, ;tart+ode, athName
Name StartMode PathName
---- --------- --------
AeLookupSvc Auto C:\Windows\system32\
svchost.ex...
AgereModemAudio Auto C:\Windows\system32\
agrsmsvc.exe
ALG Manual C:\Windows\System32\
alg.exe
Appinfo Manual C:\Windows\system32\
svchost.ex...
AppMgmt Manual C:\Windows\system32\
svchost.ex...
Ati External Event Utility Auto C:\Windows\system32\
Ati2evxx.exe
AudioEndpointBuilder Auto C:\Windows\System32\
svchost.ex...
Audiosrv Auto C:\Windows\System32\
svchost.ex...
Automatic LiveUpdate... Auto "C:\Program Files\
Symantec\Liv...
(...)
/or-ach-,b>ect gives you more options. It enables you to access all the properties and methods of
each object. The ForEach-Object cmdlet executes a block of statements for every single object in a
pipeline. Automatic variable $_ contains the current pipeline object.
Get-WmiObject 7in'2E;er4ice |
ForEach-Object 9 "{0} ({1}): Path: {2}" `
-f $_.Name, $_.StartMode, $_.PathName :
AeLookupSvc (Auto): Path: C:\Windows\system32\svchost.exe -k netsvcs
AgereModemAudio (Auto): Path: C:\Windows\system32\agrsmsvc.exe
ALG (Manual): Path: C:\Windows\System32\alg.exe
Appinfo (Manual): Path: C:\Windows\system32\svchost.exe -k netsvcs
AppMgmt (Manual): Path: C:\Windows\system32\svchost.exe -k netsvcs
(...)
&ntegrating Conditions
In the script block after /or-ach-,b>ect, all PowerShell commands and statements are permitted, so
you could output only running services along with their descriptions:
Get-WmiObject 7in'2E;er4ice |
ForEach-Object 9
if $$_.Started& 9
"{0}({1}) = {2}" -f $_.Caption, $_.Name, $_.Description
:
:
Table of Contents | About PowerShell Plus 225 Sponsors | Resources | BBS Technologies
Windows Audio Endpoint Builder = Manages audio devices
for the Windows Audio service. If this service is stopped,
audio devices and effects will not function properly. If
this service is disabled, any services that explicitly
depend on it will no longer start.
Windows-Audio(Audiosrv) = Manages audio devices for
Windows-based programs. If this service is stopped, audio
devices and effects will not function properly. If this
service is disabled, any services that explicitly depend
on it will fail to start.
Base Filtering Engine (BFE) = The Base Filtering Engine
is a service that manages firewall and Internet Protocol
security (Ipsec) policies and implements user mode
filtering. Stopping or disabling the BFE service will
significantly reduce the security of the system. It will
also result in unpredictable behavior in IPsec management
and firewall applications.
Remember the building-block principle of the pipeline and keep it
simple and modular! Although it is permitted to specify conditions
and complex instructions in the script block after /or-ach-,b>ect,
the pipeline will be easier to read and more flexible if you sub-
divide each task into separate steps and use the Where-,b>ect
cmdlet described in Chapter 7 as a condition:
Get-WmiObject 7in'2E;er4ice |
Where-Object 9 $_.Started -eq $true : |
ForEach-Object 9"{0}({1}) = {2}" -f `
$_.Caption, $_.Name, $_.Description:
Don't forget the conditions covered in Chapter 7: they must result in =true or
=7alse-that's the only requirement. If a variable already contains =true or
=7alse, its result can be used immediately. So, it doesn't matter at all
whether you formulate =3.Started -e: =true as a condition or the shorter
=3.Started, because in either case, the result will be either =true or =7alse.
Because the Where-,b>ect and /or-ach-,b>ect building blocks are often used in practice, you can
use aliases: "?" stands for Where-,b>ect and "%" stands for /or-ach-,b>ect. This won't make the
lines more readable, but they'll be shorter and easier to enter:
Get-WmiObject 7in'2E;er4ice | L 9 $_.Started : | % 9
"{0}({1}) = {2}"-f $_.Caption, $_.Name, $_.Description :
Table of Contents | About PowerShell Plus 226 Sponsors | Resources | BBS Technologies
&nvoking 'ethods
Because /or-ach-,b>ect gives you access to each object in a pipeline, you can invoke the methods
of these objects. In Chapter 7, you already learned how to take advantage of this to close all
instances of the Notepad.
Get-Process notepad | ForEach-Object 9 $_.Kill$& :
However, this instruction closes all processes called notepad, even the Notepads that you had
opened much earlier. Because PowerShell always works with objects, and because you have access
to all object properties and methods within the scope of the /or-ach-,b>ect cmdlet, you could select
just some of them. For example, you could stop only those Notepad processes that haven't been
running for longer than three minutes. How can you find out how long a process has already been
running?
Notepad
$process = ?$Get-Process notepad&F"G
$process.StartTime
Sunday, March 8, 2009 08:17:27
The time difference between the current and the start time is calculated by the 5ew-6i!eSpan
cmdlet:
New-TimeSpan $process.StartTime $Get-Date&
Days : 0
Hours : 0
Minutes : 3
Seconds : 7
Milliseconds : 766
Ticks : 1877660000
TotalDays : 0,00217321759259259
TotalHours : 0,0521572222222222
TotalMinutes : 3,12943333333333
TotalSeconds : 187,766
TotalMilliseconds : 187766
And that's how the command line could look that ends all processes called Notepad that have not
been running for longer than three minutes:
Get-Process notepad | ForEach-Object 9
$time = $New-TimeSpan $_.StartTime $Get-Date&&.TotalSecondsB
if $$time -lt !I"& 9
"Stop process $($_.id) after $time seconds..."B
$_.Kill$&
:
else 9
"Process $($_.id) has been running for " +
"$time seconds and have not be stopped."
:
Table of Contents | About PowerShell Plus 227 Sponsors | Resources | BBS Technologies
:
These lines function extremely well, but are somewhat unclear. The /or-ach-,b>ect loop contains a
condition. That's actually where Where-,b>ect can come in:
Get-Process notepad |
Where-Object 9
$time = $New-TimeSpan $_.StartTime $Get-Date&&.TotalSecondsB
$$time -lt !I"&
: |
ForEach-Object 9
"Stop process $($_.id) after $time seconds..."B
$_.Kill$&
:
This works, too. Now, while you have separated condition and loop, you have been confronted with a
disadvantage of Where-,b>ect: this cmdlet allows only those objects to pass that match your
condition. All the others will quietly vanish. That's why this approach doesn't have any option to
output a notification about processes that have already been running for a longer period of time and
have not been stopped. Perhaps you still remember from Chapter 7 that Switch combines the
features of a loop and a condition. If you need both, Switch can be a useful solution:
Switch $Get-Process notepad& 9
9
$time = $New-TimeSpan $_.StartTime $Get-Date&&.TotalSecondsB
$time -le !I"
:
9
"Stop process $($_.id) after $time seconds..."B
$_.Kill$&
:
de2a8lt 9"Process $($_.id) has been running for some time and will not be
stopped.":
:
"oreach
Aside from /or-ach-,b>ect, PowerShell also comes with the /oreach statement. At first glance, both
appear to work nearly identically. While /or-ach-,b>ect obtains its entries from the pipeline, the
/oreach statement iterates over a collection of objects:
# ForEach-Object lists each element in a pipeline:
(ir C:\ | ForEach-Object 9 $_.name :
# Foreach loop lists each element in a colection:
Foreach $$element in (ir C:\& 9 $element.name :
Table of Contents | About PowerShell Plus 228 Sponsors | Resources | BBS Technologies
And here is precisely the basic difference between them. /or-ach-,b>ect works best in a pipeline,
where each result is returned by the preceding command in real time. /oreach can only process
objects that are already completely available. /oreach blocks PowerShell until all results are
available; for complex commands that can take a very long time. /oreach processes the objects only
a7ter .ir has retrieved them:
# Foreach loop lists each element in a collection:
Foreach $$element in (ir C:\ -recurse& 9 $element.name :
Now you won't see anything at all for a long time-at most a few strange error messages. The
reason is that you have assigned .ir the task of recursively retrieving the directory listing for the
entire C:\ drive, and that can take some time. The error messages that may appear come from
directories for which you have no read rights. The /oreach loop cannot go into action until the .ir
result is completely available.
The PowerShell pipeline does a better job. In it, .ir gets its results one at a time so that /or-ach-
,b>ect can already work through them while .ir is still performing its task. This means that there's
no delay, and everything proceeds in real time. In other words, /or-ach-,b>ect processes the results
of .ir while .ir returns them:
# ForEach-Object lists each element in a pipeline:
(ir C:\ -recurse | ForEach-Object 9 $_.name :
What are the special strengths of /oreach? It is the better choice whenever the results that you want
to evaluate are already completely available, such as in a variable, because it is considerably
quicker.
Let's read all elements of an array using a Foreach loop.
# Create your own array:
$array = ',6,"Hello",!2
# Read out this array element by element:
Foreach $$element in $array& 9"Current element: $element":
Current element: 3
Current element: 6
Current element: Hello
Current element: 12
/or-ach-,b>ect and the pipeline could also iterate through an
array:
$array = ',6,"Hello",!2
$array | ForEach-Object 9 "Current element: $_" :
But Foreach is significantly quicker. You can find out how dramatic the time
advantage is by using Measure-Command cmdlet:
$Measure-Command 9
$array | ForEach-Object 9 "Current element: $_" :
Table of Contents | About PowerShell Plus 229 Sponsors | Resources | BBS Technologies
:&.totalmilliseconds
2.8
$Measure-Command 9
Foreach $$element in $array& 9"Current element: $element":
:&.totalmilliseconds
0.2
If the objects are already in a variable, it's more than 10 times faster to use
/oreach to evaluate them directly than to drive them through the pipeline.
The following rules can be deduced:
"orEach(*bJect: If you have to acquire the results first, and if this acquisition lasts longer
than a few milliseconds, then use /or-ach-,b>ect and the pipeline so that you won't have to
wait for long periods of time and the results are processed immediately where they are
available.
"oreach: If you have the results already available in a variable or if their acquisition is very
fast, then use /oreach because of its speed advantage.
/oreach functions in principle with any kind of collection. For example, you could use .ir to obtain a
directory listing and then use /oreach to further process each file and directory:
# Process all files and subdirectories in a directory separately:
Foreach $$entry in dir c:\& 9
# Either embed the data as subexpressions in a text:
"File $($entry.name) is $($entry.length) bytes large"
# Or use wildcards and the -f formatting operator:
"File {0} is {1} bytes large" -f $entry.name, $entry.length
:
File autoexec.bat is 24 bytes large
File autoexec.bat is 24 bytes large
File BOOTSECT.BAK is 8192 bytes large
File BOOTSECT.BAK is 8192 bytes large
(...)
The /oreach loop can also handle empty collections and even
objects that aren't even collections. If .ir doesn't retrieve any files
at all, the loop won't run a single time. If .ir returns just one file,
then /oreach will automatically recognize that this is one single
object, and it will run the loop exactly one time.
Table of Contents | About PowerShell Plus 230 Sponsors | Resources | BBS Technologies
You could just as well have used the 'et-W!i,b>ect cmdlet to look for instances of a WMI class and
had it retrieve all running services on your system. /oreach would then examine each of the services
and generate a list with the general service names, as well as the localized service names:
# Use WMI to query all services of the system:
$services = Get-WmiObject 7in'2E;er4ice
# Output the Name and Caption properties for every service:
Foreach $$service in $services& 9 $service.Name +
" = " + $service.Caption :
AeLookupSvc = Application Experience Lookup
AgereModemAudio = Agere Modem Call Progress Audio
ALG = Application Layer Gateway Service
Appinfo = Application Information
(...)
That, however, pushes things to the limit because 'et-W!i,b>ect may require several seconds in
some circumstances. It would probably be better for you to use /or-ach-,b>ect.
In principle, /oreach requires only a collection of objects. Such collections, when you look closely,
are widely distributed. The Resol$e-Path cmdlet uses wildcards to change a path specification to an
array with all actual paths matching the wildcard characters. The next example lists all the text files
in your user profile:
Resolve-Path -Path "$home\*.txt"
Path
----
C:\Users\Tobias Weltner\output.txt
C:\Users\Tobias Weltner\cmdlet.txt
C:\Users\Tobias Weltner\error.txt
C:\Users\Tobias Weltner\list.txt
C:\Users\Tobias Weltner\snapshot.txt
The /oreach loop could now go through the result of Resol$e-Path and open every single file it found
in the Notepad:
function open-editor $FstringG$path="$home\*.txt"& 9
$list = Resolve-Path -Path $path
Foreach $$file in $list& 9
"Open File $file..."
notepad $file
:
:
This line would then open all log files in your Windows subdirectory in the Notepad:
open-editor $env:windir\*.log
Now and then, commands like .ir (or 'et-"hildite!) retrieve several different object types, /ile4n7o
objects for files and .irectory4n7o objects for directories. That doesn't matter to /oreach: every time
a loop cycle is completed, /oreach will get an object until all objects are processed. However, it
Table of Contents | About PowerShell Plus 231 Sponsors | Resources | BBS Technologies
should matter a little to you, and so you could use a condition to test whether the retrieved object
matches the desired type. The following loop gets different objects depending on whether it is a
directory or a file:
# Process all files and subdirectories in a directory one by one:
Foreach $$entry in dir c:\& 9
# Is it a FileInfo object?
if $$entry -i6 FSystem.IO.FileInfoG& 9
# If yes, output name and size:
"File {0} is {1} bytes large" -f $entry.name, $entry.length
:
# Or is it perhaps a DirectoryInfo object?
elseif $$entry -i6 FSystem.IO.DirectoryInfoG& 9
# If yes, output name and creation time:
"Subdirectory {0} was created on {1:}" -2 $entry.name,
$entry.CreationTime
:
:
Documents and Settings subdirectory was created on
08.28.2006 19:15:14
Program Files subdirectory was created on 11.02.2006 12:18:33
Programs subdirectory was created on 08.28.2006 19:15:47
Users subdirectory was created on 11.02.2006 12:18:33
Windows subdirectory was created on 11.02.2006 12:18:34
autoexec.bat file is 24 bytes large
BOOTSECT.BAK file is 8192 bytes large
config.sys file is 10 bytes large
)o and 3hile
.o and While generate endless loops. Endless loops are practical if you don't know exactly how long
the loop should iterate. To prevent an endless loop to really run endlessly, you must set additional
abort conditions. The loop will end when the conditions are met.
Continuation and Abort Conditions
A typical example of an endless loop is a user query that you want to iterate until the user gives a
valid answer. How long that lasts and how often the query will iterate depends on the user and his
ability to grasp what you want.
Do 9
$input = Read-Host "Your homepage"
: While $!$$input -like "www.*.*"&&
Table of Contents | About PowerShell Plus 232 Sponsors | Resources | BBS Technologies
This loop asks the user for his home page Web address. At the end of the loop after While is the
criteria that has to be !et so that the loop can be iterated once again. In the example, -like is used
to verify whether the input matches the www.*.* pattern. While that's only an approximate
verification, usually it suffices. To refine your verification you could also use regular expressions.
Both procedures will be explained in detail in Chapter 13.
This loop is supposed to iterate only if the input is 7alse. That's why "!" is used to simply invert the
result of the condition. The loop will then be iterated until the input does not match a Web address.
In this type of endless loop, verification of the loop criteria doesn't take place until the end. The loop
will go through its iteration at least once, because before you can check the criteria, you have to
query the user at least once.
However, there are also cases in which the criteria is supposed to be verified at the beginning and
not at the end of the loop, namely whenever there are certain conditions when the loop must not go
through any iteration. An example could be a text file that you want to read one line at a time. The
file could be empty and the loop should check before its first iteration whether there's anything at all
to read. To accomplish this, just put the While statement and its criteria at the beginning of the loop
(and leave out .o, which is no longer of any use):
# Open a file for reading:
$file = Fsystem.io.fileG::OpenText$"C:\autoexec.bat"&
# Continue loop until the end of the file has been reached:
While $!$$file.EndOfStream&& 9
# Read and output current line from the file:
$file.ReadLine$&
:
# Close file again:
$file.close
8sing Iariables as Continuation Criteria
The fact is that the continuation criteria after While works like a simple switch. If the expression is
=true, then the loop will be iterated; if it is =7alse, then it won't. Conditions are therefore not
obligatory, but just simply provide the required =true or =7alse. You could just as well have
presented the loop with a variable as criteria as long as the variable contained =true or =7alse.
In such a way, you could have verified the criteria in the loop as well and stored the result in a
variable. Then you could have used the verification result in the loop and output an explanatory text
when the user gave false input so that he would know why he was being queried a second time:
Do 9
$input = Read-Host "Your Homepage"
if $$input -li>e "www.*.*"& 9
# Input correct, no further query:
$furtherquery = $false
: else 9
# Input incorrect, give explanation and query again:
Write-Host -/ore "Red" "Please give a valid web address."
$furtherquery = $true
:
Table of Contents | About PowerShell Plus 233 Sponsors | Resources | BBS Technologies
: While $$furtherquery&
Your Homepage: hjkh
Please give a valid web address.
Your Homepage: www.powershell.com
Endless Loops without Continuation Criteria
In extreme cases, you should not use any continuation criteria at all but simply type the fixed value
=true after While. The loop will then become a genuinely endless loop, which from then on will no
longer stop on its own. Of course, that makes sense only if you exit the loop in some other way. The
break statement makes that possible:
While $$true& 9
$input = Read-Host "Your homepage"
if $$input -li>e "www.*.*"& 9
# Input correct, no further query:
break
: else 9
# Input incorrect, give explanation and ask again:
Write-Host -/ore "Red" "Please give a valid web address."
:
:
Your homepage: hjkh
Please give a valid web address.
Your homepage: www.powershell.com
"or
If you know exactly how often you want to iterate a particular code segment, then use the /or loop.
/or loops are counting loops, and when the loop is iterated often enough, it will end its iterations
automatically. To define the number of iterations, specify the number at which the loop begins and
at which number it will end, as well as which increments will be used for counting. The following loop
will retrieve exactly seven lottery numbers for you. It begins counting at 0, counts until the value is
less than seven, and increases the value by one with every new iteration.
# Create random number generator
$random = New-Object system.random
# Output seven random numbers from 1 to 49
/or $$i="B $i -lt *B $i++& 9
$random.next$!,#A&
:
32
29
44
43
6
Table of Contents | About PowerShell Plus 234 Sponsors | Resources | BBS Technologies
38
9
"or Loops: Qust Special T!pes o0 the 3hile Loop
If you take a closer look at the /or loop, you'll quickly notice that it is actually only a specialized
form of the While loop. The /or loop, in contrast to the While loop, evaluates not only one but three
expressions:
&nitiali.ation: The first expression is evaluated when the loop begins.
Continuation criteria: The second expression is evaluated before every iteration. It
basically corresponds to the continuation criteria of the While loop. If this expression is =true,
the loop will iterate.
&ncrement: The third expression is likewise re-evaluated with every looping, but it is not
responsible for iterating. Be careful: this expression cannot generate output.
These three expressions are used to initialize a control variable, to verify whether a final value is
achieved, and to change a control variable with a particular increment at every iteration of the loop.
Of course, it is entirely up to you whether you want to use the /or loop solely for this purpose.
A /or loop can become a While loop if you ignore the first and the third expression and only use the
second expression, the continuation criteria:
# First expression: simple While loop:
$i = "
While $$i -lt %& 9
$i++
$i
:
1
2
3
4
5
# Second expression: the For loop behaves like the While loop:
$i = "
/or $B$i -lt %B& 9
$i++
$i
:
1
2
3
4
5
Table of Contents | About PowerShell Plus 235 Sponsors | Resources | BBS Technologies
8nusual 8ses 0or the "or Loop
Of course, it might have been preferable in this case to use the While loop right from the beginning.
It certainly makes more sense not to ignore the other two expressions of the /or loop, but to use
them for other purposes. The first expression of the /or loop can be used in general for initialization
tasks. The third expression could set the increment of a control variable as well as perform different
tasks in the loop. You could also use it, in fact, in the user query example we just had:
/or $$input=""B !$$input -like "www.*.*"&B
$input = Read-Host "Your homepage"& 9
Write-Host -fore "Red" " Please give a valid web address."
:
In the first expression, the =input variable is set to an empty string. The second expression checks
whether a valid Web address is in =input, and if it is, it uses "!" to invert the result so that it is =true
if an invalid Web address is in =input. In this case, the loop is iterated. In the third expression, the
user is queried for a Web address. Really nothing more needs to be in the loop. In the example, an
explanatory text is output.
In addition, the line-by-line reading of a text file can be implemented by a /or loop with less code:
/or $$file = Fsystem.io.fileG::OpenText$"C:\autoexec.bat"&B
!$$file.EndOfStream&B $line = $file.ReadLine$&&
9
# Output read line:
$line
:
$file.close$&
REM Dummy file for NTVDM
In this example, the first expression of the loop opened the file so it could be read. In the second
expression, a check is made whether the end of the file has been reached. The "!" operator inverts
the result again so that it returns =true if the end of the file hasn't been reached yet so that the loop
will iterate in this case. The third expression reads a line from the file. The read line is then output in
the loop.
The third expression of the /or loop is executed before every loop
cycle. In the example, the current line from the text file is read.
This third expression is always executed invisibly; that means you
can't use it to output any text. So, the contents of the line are
output within the loop.
Table of Contents | About PowerShell Plus 236 Sponsors | Resources | BBS Technologies
Switch
Do you still remember the Switch statement discussed in Chapter 7? Switch is not only a condition
but also functions like a loop. That makes Switch one of the most powerful statements in
PowerShell. Switch works almost exactly like the /oreach loop. Moreover, it can evaluate conditions.
For a demonstration, take a look at the following simple /oreach loop:
$array = !..%
Foreach $$element in $array&
9
"Current element: $element"
:
Current element: 1
Current element: 2
Current element: 3
Current element: 4
Current element: 5
If you used Switch, this loop would look like this:
$array = !..%
Switch $$array&
9
(e2a8lt 9 "Current element: $_" :
:
Current element: 1
Current element: 2
Current element: 3
Current element: 4
Current element: 5
The control variable that returns the current element of the array for every loop cycle cannot be
named for Switch, as it can for /oreach, but is always called =3. The external part of the loop
functions in exactly the same way. Inside the loop, there's an additional difference: while /oreach
always executes the same code every time the loop cycles, Switch can utilize conditions to execute
optionally different code for every loop. In the simplest case, the Switch loop contains only the
de7ault statement. The code that is to be executed follows it in braces.
That means /oreach is the right choice if you want to execute exactly the same statements for every
loop cycle anyway. On the other hand, if you'd like to process each element of an array according to
its contents, it would be preferable to use Switch:
$array = !..%
Switch $$array&
9
! 9 "The number 1" :
9$_ -lt ': 9 "$_ is less than 3" :
9$_ % 2: 9 "$_ is odd" :
(e2a8lt 9 "$_ is even" :
Table of Contents | About PowerShell Plus 237 Sponsors | Resources | BBS Technologies
:
The number 1
1 is less than 3
1 is odd
2 is less than 3
3 is odd
4 is even
5 is odd
If you're wondering why Switch returned this result, take a look at Chapter 7 where you'll find an
explanation of how Switch evaluates conditions. What's important here is the other, loop-like aspect
of Switch.
Processing "ile Contents Line b! Line
If you need conditions in your loop, Switch is a clever alternative to /oreach. The same thing is true
when you want to process text files, because if you wish Switch will treat a text file like an array and
the lines it contains like elements in the array. This means that you don't have to worry about how
to open files for reading; you can just leave that up to Switch.
For example, an interesting text file is windowsupdate.log in the Windows subdirectory because it
records all updates of the operating system. Because the system often has exclusive access to this
file, the following code copies the file and then uses Switch to output its contents line by line.
Afterwards, the copy is deleted:
Copy-Item $env:windir\windowsupdate.log example.log
Switch -file example.log
9
(e2a8lt 9 "read: $_" :
:
Remove-Item example.log
Switch is really too sophisticated a tool for just opening a text file and outputting its contents line by
line. If all you were interested in was the entire text content of the file, you could have output it
more easily:
Get-Content $env:windir\windowsupdate.log
The strength of Switch lies in its ability to evaluate single lines of a text file and then output only
particular data. Because this is really a case for regular expressions, you'll find numerous examples
in Chapter 13.
Exiting Loops Earl!
You can exit all loops by using the 9reak statement, which gives you the additional option of defining
additional stop criteria in the loop. The following is a little example that asks for a password and
then uses 9reak to exit the loop as soon as the password "secret" is entered.
Table of Contents | About PowerShell Plus 238 Sponsors | Resources | BBS Technologies
While $$true&
9
$password = Read-Host "Enter password"
If $$password -eq "secret"& 9break:
:
The 9reak statement is actually unnecessary in this loop because you could have also stopped the
loop by using the usual continuation criteria. You just have to consider here whether the iteration
criteria should be verified at the beginning (and then you'd use While) or at the end (and then
.o...While) of the loop:
Do
9
$password = Read-Host "Enter password"
: While $$password -ne "secret"&
It would make more sense to use 9reak in /or loops, because in /or loops you can optimally
combine the unscheduled 9reak with the scheduled iteration criteria of the loop. Perhaps you'd like
to give users just three tries at entering a correct password. The following loop asks for a password
a maximum three times, but can, thanks to 9reak, be exited earlier when the correct password is
entered:
/or $$i="B $i -lt 'B $i++&
9
$password = Read-Host "Enter password ($i. try)"
If $$password -eq "secret"& 9break:
:
But the /or loop would not only give up after a maximum three tries, but would also grant you
access even without the right password. To prevent that from happening, after the third
unsuccessful try, you should trigger an error:
/or $$i=!B $i -lt #B $i++&
9
$password = Read-Host "Enter password ($i. try)"
If $$password -eq "secret"& 9break:
If $$i -ge '& 9 3hro5 "The entered password was incorrect." :
:
What you see here is only a very simple password query showing
the password in plain text. Secure password queries that have
encrypted input will be covered in Chapter 13 in connection with
the feature called SecureStrins.
Table of Contents | About PowerShell Plus 239 Sponsors | Resources | BBS Technologies
Continue: Skipping Loop C!cles
The "ontinue statement operates somewhat more mildly than 9reak, because "ontinue won't force
you to exit the entire loop right away but will only skip the current loop cycle. Let's look at the
/oreach loop that cycles through all elements of a collection. In this case, .ir will supply the
collection and the collection will hold the contents of a directory. These contents can consist of files
and subdirectories; and, as you should know by now, files are represented by a /ile4n7o and sub-
directories by a .irectory4n7o object.
So, when you want to process just files and not directories in the /oreach loop, then you should
initially verify the type of the respective object. If the type doesn't match the /ile4n7o object, provide
the "ontinue statement: the loop will then stop its current cycle immediately and continue with the
next element:
Foreach $$entry in (ir $env:windir&
9
# If the current element matches the desired type,
# continue immediately with the next element:
If $!$$entry -is FSystem.IO.FileInfoG&& 9 Continue :
"File {0} is {1} bytes large." -f $entry.name, $entry.length
:
Of course, you could have also achieved the same thing if you had used a condition to sub-divide the
entire contents of the loop, though usually that is substantially less clear:
Foreach $$entry in (ir $env:windir&
9
If $$entry -is FSystem.IO.FileInfoG& 9
"File {0} is {1} bytes large." -f $entry.name, $entry.length
:
:
7ested Loops and Labels
Loops may be nested within each other. However, if you do nest loops, the question arises of how
their 9reak and "ontinue statements will behave. Of course, they will behave for the time being the
way you expect them to and will always have an effect on the current loop in which they were
invoked.
The next example nests two /oreach loops. The first (outer) loop cycles through a field with three
WMI class names. The second (inner) loop runs through all instances of the respective WMI class. In
this way, you could output all instances of all three WMI classes. The inner loop checks whether the
name of the current instance begins with "a"; if not, the inner loop invokes "ontinue and so skips all
instances not beginning with "a." The result is a list of all services, user accounts, and running
processes that begin with "a":
Foreach $$wmiclass in "Win32_Service","Win32_UserAccount","Win32_Process"&
9
Foreach $$instance in Get-WmiObject $wmiclass& 9
If $!$$$instance.name.toLower$&&.StartsWith$"a"&&& 9continue:
Table of Contents | About PowerShell Plus 240 Sponsors | Resources | BBS Technologies
"{0}: {1}" -2 $instance.EECL.;;, $instance.name
:
:
Win32_Service: AeLookupSvc
Win32_Service: AgereModemAudio
Win32_Service: ALG
Win32_Service: Appinfo
Win32_Service: AppMgmt
Win32_Service: Ati External Event Utility
Win32_Service: AudioEndpointBuilder
Win32_Service: Audiosrv
Win32_Service: Automatic LiveUpdate - Scheduler
Win32_UserAccount: Administrator
Win32_Process: Ati2evxx.exe
Win32_Process: audiodg.exe
Win32_Process: Ati2evxx.exe
Win32_Process: AppSvc32.exe
Win32_Process: agrsmsvc.exe
Win32_Process: ATSwpNav.exe
As expected, the "ontinue statement in the inner loop had an effect on the inner loop in which the
statement was contained. But how should you proceed if you'd like to see only the first respective
element of all services, user accounts, and processes that begins with "a"? Actually, nearly the exact
same way, only in this case "ontinue would have to have an effect on the outer loop. As soon as an
element is found that begins with "a," the outer loop should jump to the next WMI class.
So that statements like "ontinue or 9reak know which loop they are supposed to relate to, you
should give loops unambiguous names and then specify these names after "ontinue or 9reak:
:WMIClasses Foreach $$wmiclass in
"Win32_Service","Win32_UserAccount","Win32_Process"& 9
:ExamineClasses Foreach $$instance in
Get-WmiObject $wmiclass& 9
If $$$instance.name.toLower$&&.StartsWith$"a"&& 9
"{0}: {1}" -2 $instance.EECL.;;, $instance.name
continue WMIClasses
:
:
:
Win32_Service: AeLookupSvc
Win32_UserAccount: Administrator
Win32_Process: Ati2evxx.exe
Table of Contents | About PowerShell Plus 241 Sponsors | Resources | BBS Technologies
Summar!
The cmdlet /or-ach-,b>ect gives you the option of processing single objects of the PowerShell
pipeline, such as to output the data contained in object properties as text or to invoke methods of
the object. /oreach is a similar type of loop whose contents do not come from the pipeline, but from
an array or a collection.
In addition, there are endless loops that iterate a code block until a particular condition is met. The
simplest type of such loops is While, in which continuation criteria are checked at the beginning of
the loop. If you want to do the checking at the end of the loop, choose .o...While. The /or loop is an
extended While loop, because it can count loop cycles and automatically terminate the loop after a
designated number of iterations.
This means that /or is suited mainly for loops in which counts are to be made or which must
complete a set number of iterations. .o...While and While, on the other hand, are suited for loops
that have to be iterated as long as the respective situation and running time conditions require it.
Finally, Switch is a combined /oreach loop with integrated conditions so that you can immediately
implement different actions independently of the read element. Moreover, Switch can step through
the contents of text files line by line and evaluate even log files of substantial size.
All loops can exit ahead of schedule with the help of 9reak and skip the current loop cycle with the
help of "ontinue. In the case of nested loops, you can assign an unambiguous name to the loops
and then use this name to apply 9reak or "ontinue to nested loops.
Table of Contents | About PowerShell Plus 242 Sponsors | Resources | BBS Technologies
CHAPTER 9.
$unctions
$unctions
PowerShell has the purpose of solving problems, and the smallest tool it comes equipped with for
this is commands. By now you should be able to appreciate the great diversity of the PowerShell
command repertoire: in the first two chapters, you already learned how to use the built-in
PowerShell commands called cmdlets, as well as innumerable external commands, such as pin or
ipcon7i. In Chapter 6, the objects of the .NET framework, and COM objects were added, providing
you with a powerful arsenal of commands.
In Chapters 3, 4, and 5, command chains forged out of these countless single commands combined
statements either by using variables or the PowerShell pipeline.
The next highest level of automation is functions, which are self-defined commands that internally
use all of the PowerShell mechanisms you already know, including the loops and conditions covered
in the last two chapters.
Topics Covered:
Creating New Functions
First Example: Shorthand Functions
Second Example: Combining Several Steps
Comfortably Entering Functions of Several Lines
Reducing a Function to a Single Line
Using Text Editors
Understanding NextFreeDrive
Processing and Modifying Functions
Removing Functions
Passing Arguments to Functions
$args: Arbitrary Arguments
Using the Argument Parser of $args
Setting Parameters
Arguments Having Predefined Default Values
Using Strongly Typed Arguments
Only Numbers Allowed
Date Required
"Switch" Parameter Is Like a Switch
Specifying Return Values of a Function
One or More Return Values?
The Return Statement
Accessing Return Values
Excluding Output from the Function Result
Excluding Text Output from the Result
Using Debugging Reports
Suppressing Error Messages
Inspecting Available Functions
Table 9.1: Predefined PowerShell functions
Prompt: A Better Prompt
Outputting Information Text at Any Location
Table of Contents | About PowerShell Plus 243 Sponsors | Resources | BBS Technologies
Using the Windows Title Bar
Administrator Warning
Clear-Host: Deleting the Screen Buffer
Predefined Functions Once Again: A:, B:, C:
Functions, Filters and the Pipeline
The Slow Sequential Mode: $input
Filter: Rapid Streaming Mode
Developing Genuine Pipeline Functions
Summary
Creating 7ew "unctions
Functions are self-defined new commands consisting of general PowerShell building blocks. They
have in principle three tasks:
Shorthand: very simple shorthand for commands and immediately give the commands
arguments to take along
Combining: functions can make your work easier by combining several steps
Encapsulating and extending: small but highly complex programs consisting of many
hundreds of statements and providing entirely new functionalities
The basic structure of a function is the same in all three instances: after the /unction statement
follows the name of the function, and after that the PowerShell code in braces. Let's take a look at
couple of examples:
"irst Example: Shorthand "unctions
Perhaps you'd simply like to create comfortable shorthand for the customary console commands you
already know. If PowerShell doesn't accept the ""d..B entry because the mandatory blank character
isn't interposed between command and argument, then create the appropriate shorthand function on
the spot-and the problem will be solved right away:
Function Cd.. 9 Cd .. :
Cd..
Whenever you enter the "d.. command afterwards, you won't get any error messages because
PowerShell will invoke your function.
When you find yourself still repeatedly entering the same lengthy commands, functions may be the
remedy. For example, if you're frequently using ping.exe with certain parameters, like pin.e(e -w
100 -n 1 10.10.10.10, then this function will save you time:
Function m<ing 9 ping.exe -w !"" -n ! 10.10.10.10 :
m<ing
Pinging 10.10.10.10 with 32 bytes of data:
Reply from 88.70.64.1: destination host unreachable.
Table of Contents | About PowerShell Plus 244 Sponsors | Resources | BBS Technologies
However, this function would be inflexible in practice; it would ping the same network address again
and again. That's why most functions use arguments. Everything the caller specifies after the
function name is in the =ars variable. Let's modify our !yPin function to ping any address.
Function m<ing 9 ping.exe -w !"" -n ! :args :
m<ing www.microsoft.com
Pinging lb1.www.ms.akadns.net [207.46.193.254] with 32 bytes of data:
Request timed out.
Ping statistics for 207.46.193.254:
Packets: Sent = 1, Received = 0, Lost = 1 (100% Loss),
As you see, you only need to type the function again in order to overwrite the old version.
Second Example: Combining Several Steps
You might often need the nearest unallocated drive letter. The 5e(t/ree.ri$e function can locate it.
But before you try out the function and think about how to make it work, you should first answer the
question of how to enter such a lengthy function.
Function Ne-t/ree(ri4e
9
/or $$x=6*B $x -le A"B $x++&
9
$driveletter = FcharG$x + ":"
If $!$Test-Path $driveletter&&
9
$driveletter
break
:
:
:
Com0ortabl! Entering "unctions o0 Several Lines
Typing short functions is no problem but when a function consists of more than one line, PowerShell
immediately activates its multiline mode, alerting you by the prompt symbol ">>":
Function Ne-t/ree(ri4e
>> 9
>> /or $$x=6*B $x -le A"B $x++&
$...&
Once the multiline mode is turned on, you have to type the entire function to the end. The prompt
symbol ">>" will appear a last time, but when you press (Enter), the function will be operational.
This kind of typing is not very user friendly, and when you make a typing mistake somewhere and
forget a brace or quotation mark, you won't even be able to exit the multiline mode. Then it's time
to cancel the multiline mode by hitting (Ctrl)+(C) and to begin all over again or to think about other
options.
Table o