Windows DevCenter    
 Published on Windows DevCenter (http://www.windowsdevcenter.com/)
 See this if you're having trouble printing code examples


Top 10 Tips for Using Windows PowerShell

by Jeff Cogswell
11/07/2006

PowerShell is Microsoft's newest replacement for the command line. It's still in beta, and the latest version is available free for download here. For us older folks, we've had to deal with DOS in the past, and then we've held on to the CMD.exe program, which is pretty much the same as a DOS command prompt running under Windows. PowerShell is not an updated version of DOS. Rather, it's a complete command-line system that is far more powerful than any command-line prompt Microsoft has given us before. Starting to learn it, unfortunately, can be a bit overwhelming at first. After you've installed it and explored it a bit, the following tips will help make your life much easier.

1. Remember the consistencies

Remember that commands have a verb-noun format and parameter names start with a -. You can use either / or \ in paths.

Probably the most basic, fundamental rule in using PowerShell is that the commands all have a common format. When you remember the format, you can more easily remember the commands.

The format is verb-noun. For example:

Set-Date
Write-Debug
Get-Item
Get-WmiObject

Each of these is a verb followed by a hyphen followed by a noun. Further, the noun is singular, even if it seems like it should be plural. Thus:

Get-Process

returns all the running processes.

But the consistency goes beyond just the names. Parameters are consistent. In the worlds of DOS and Unix, commands either use - or / to denote a parameter name. In PowerShell, all parameter names start with -.

However, path names, on the other hand, can be separated by either a forward slash or a backward slash. You can use either, whichever you're most comfortable with. If you need to combine paths, however, you can easily use the built-in command join-path like so:

join-path c:\ \temp

which results in c:\temp. (This is handy because you don't have to manually remove the extra slashes that might result in just combining the two strings.)

2. Remember the useful commands

Use Get-Command to get a list of commands and Get-Help to find out help on the commands. You can also pass -? as a parameter to a command to obtain help. Use TabExpansion (and, optionally, find a TabExpansion replacement).

Whenever you're using a new language, you want to know the vocabulary. The second release candidate of PowerShell contains 129 commands. That's a lot to learn and memorize. But you'll want to be at least familiar with what's available, and you'll want to make sure you know some of the more common ones. To see all the commands type

Get-Command

One command is Get-Help. You can use this to find out help on a particular command:

Get-Help Get-Member

will display help on the command Get-Member. Or you can pass -? as a parameter like so:

Get-Member -?

The built-in commands also provide different levels of help. The default help provides basic information. You can get more help providing details on the parameters along with examples by adding -detailed to the Get-Help command:


Get-Help Get-Member -detailed

And you can get even more help by typing -full:

Get-Help Get-Member -full

Since many commands are long and we, as computer people, usually don't want to type huge commands, PowerShell includes an alias feature along with several built-in aliases. You can list all the aliases like so:


PS C:\WINDOWS\system32> Get-Alias

CommandType     Name            Definition
-----------     ----            ----------
Alias           ac              Add-Content
Alias           asnp            Add-PSSnapin
Alias           clc             Clear-Content
Alias           cli             Clear-Item
Alias           clp             Clear-ItemProperty
...

The Tab key has a special usage that helps you find commands. If you remember the verb, you can type the verb then the hyphen, and press Tab repeatedly to cycle through the different possible commands starting with that verb. If you type:

Get-

and then start pressing Tab, you'll see Get-Acl, then Get-Alias, then Get-AuthenticodeSignature, and so on.

However, the built-in tab expansion feature isn't that sophisticated, and Microsoft knows it. That's why it made a function called TabExpansion that can be modified. Search the web for PowerShell TabExpansion (no space in the second word) and see what's out there. People have written TabExpansion replacements and there are some pretty good ones out there. Find one that suits your needs. (Incidentally, you can see the source for the TabExpansion function using Get-Content function:TabExpansion.)

Here's a list of some of the commands I find myself using often. When you use commands, you'll probably want to know any available aliases as well.

Command Aliases

Command Aliases
Copy-Item cpi, cp, copy
ForEach-Object foreach, %
Get-ChildItem dir, ls
Get-Command gcm
Get-Content cat, type
Get-Content gc, type, cat
Get-Help help
Get-Location gl, pwd
Move-Item mi, mv, move
Remove-Item ri, rd, del, rm, rmdir
Rename-Item rni, ren
Set-Location cd, chdir
Where-Object where

Get-Command takes some parameters to help you find the commands you need. For example, if you're looking for commands that have the word Format in them, you can type:

 Get-Command *Format*

However, you might only want the commands that start with the verb Format (such as Format-Table). This will work:

 Get-Command -verb Format

That will return the commands whose verb is exactly Format. If you type

Get-Command -verb Form

you will not get back those with the verb Format unless you use a wildcard pattern:

Get-Command -verb Form*

You can similarly specify the noun:

Get-Command -noun Alias

will list all commands dealing with aliases.

There's also a couple handy single-character aliases. % is an alias for ForEach-Object and ? is an alias for Where-Object.

3. Learn the fundamental types and built-in variables

Learn the fundamental types and how the parser recognizes them. Master regular expressions so your life will be complete.

The PowerShell scripting language includes several basic types that are useful in your work. And, you can create variables of these types. You have at your disposal all the built-in types in .NET. This includes, for example, strings, numbers, arrays, and dictionaries (which some people know as hashes or maps). The types are all built around the .NET types. (I talk about .NET in the next section.)

The command parser will decide the types based on what you type. For example, if you type

5

you will be specifying an integer. Variable names get a $ before them, and you can store the integer like so:

PS C:\> $a = 5
PS C:\> $a
5
PS C:\>

You can determine the built-in .NET type by calling GetType():

PS C:\> $a.GetType()
IsPublic IsSerial Name              BaseType
-------- -------- ----              --------
True     True     Int32             System.ValueType

The parser also recognizes other basic types. Doubles can be created with a decimal point:

PS C:\> $b = 3.1415926
PS C:\> $b
3.1415926
PS C:\> $b.GetType().Name
Double

Like many of today's scripting languages, you can use either single or double quotes for strings, whichever is convenient for the problem at hand. For example, if you need double-quotes in the string, you can use single quotes for the string itself:

$c = '<span style="border:1px solid none">'

To create an array, just provide a list of comma-separated items:

PS C:\> $d = 1,2,3,"Hello"
PS C:\> $d
1
2
3
Hello
PS C:\> $d.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

If necessary, you can put parentheses around the array. This is useful for arrays of arrays:

$e = (1,2,3,(4,5,6))

You can specify ranges of integers using the .. operator like so:

$f = 1..5

will create an array containing 1,2,3,4,5.

To create a dictionary, use the @ symbol like so:

PS C:\> $myhash = @{ 'one'=1; 'two'=2; 'three'=3 }
PS C:\> $myhash

Name                           Value
----                           -----
two                            2
three                          3
one                            1

PS C:\> $myhash['one']
1
PS C:\> $myhash['four']
PS C:\>

Since .NET includes powerful regular expression capabilities, PowerShell includes a regex type. Just put the word regex in brackets ([]) before a string to create a regular expression. Then you can use the regular expression with the -match operators. Here's an example:

PS C:\> $re = [regex]"abc[123]"
PS C:\> "abc1" -match $re
True
PS C:\> "abc4" -match $re
False
PS C:\>

If you're not familiar with regular expressions, then now is a great time to learn them. The more you know regular expressions, the easier time you'll have with PowerShell as well as many programming languages. Go to your favorite search engine and type in "learn regular expressions". There are several good pages out there. But probably the best source is the O'Reilly book Mastering Regular Expressions by Jeffrey Friedl. This is one of the few books that should be required reading for every computer professional.

The shell includes some built-in variables that you can use, too. Here are some of them:

(Scite is the free text editor I use.)

If you want to use a specific type without relying on the parse, PowerShell comes with several built-in type shortcuts. A type shortcut is a shortened form of a type name. The Microsoft PowerShell's blog includes an entry listing some of them.

Learning and understanding the types can be unwieldy, however, especially if a command returns an object of a type you're not familiar with. If you're savvy with reading online helps, this blog entry is a way to enhance your PowerShell so you can access the online help for any object's type.

(If you make this change, you'll have to re-sign your file. See the tip "Customize your environment" for more information.) When you implement this modification, you can find the online help for any object's type. The technique isn't perfect and doesn't work for every object, but it does work most of the time.

4. Know your .NET

The .NET framework is the foundation on which PowerShell runs. The more you know about .NET, the better your PowerShell experience will be.

To get the most out of PowerShell, you should be familiar with the .NET framework. The framework's help is available online in its entirety at . Scroll down to the System heading. This is where you'll find the basic types such as Int32, String, and Array.

Remember, the framework is simply a huge collection of classes and types that you can use in your PowerShell work. The more you know of these types, the better off you are. If you look up entries on your types, you'll know what you can do with objects. For example, if you click the page for System, scroll down and click String, you'll get the entry for the String class.

Most of the commands return an object (or a set of objects) that are, in fact, .NET objects. For example, the Get-Date command on the surface may appear to just print out the current date and time. But in fact, it returns an object of type DateTime, which is part of the .NET framework. Similarly, if you type Get-ChildItem (or an alias, dir), you see a directory listing, but in fact, are looking at a list of objects, each of which is either a DirectoryInfo or a FileInfo object. These are .NET classes.

The .NET framework is organized into a huge hierarchy, with all classes being derived from Object. The Object class provides some base functionality common to every single class in .NET. One base functionality is a function called ToString. This function returns a text representation of an object. The way this pertains to PowerShell is that given any object, you can always get a string representation of it. (In fact, this is exactly how PowerShell prints out an object to the console.)

5. Understand the pipeline is object-based not text-based

Remember that objects are passed through the pipeline, not just text, which means a great deal of information is available to you at any point in the pipe.

If you've worked with other shells such as cmd in Windows or any of the Unix-based shells, you know that commands can output text, which can in turn be passed (piped) to other commands. For example, in cmd and DOS, the dir command prints out a list of files. This list is in the form of text. If you just run the dir command, you'll see the list appear in the console window. However, you can instead send that text as input into another command; this is called piping. The more command, for example, reads in text and writes out only a page worth, and then waits for a keypress before writing another page worth of text:

C:\WINDOWS >dir /ad | more
 Volume in drive C has no label.
 Volume Serial Number is BCFB-2313

 Directory of C:\WINDOWS

11/04/2006  10:59 PM    <DIR>          .
11/04/2006  10:59 PM    <DIR>          ..
06/07/2004  07:03 PM    <DIR>          $NtUninstallKB824105$
06/07/2004  07:05 PM    <DIR>          $NtUninstallKB824141$
10/15/2004  09:06 PM    <DIR>          $NtUninstallKB824151$
06/07/2004  07:01 PM    <DIR>          $NtUninstallKB825119$
06/07/2004  07:00 PM    <DIR>          $NtUninstallKB828035$
06/07/2004  06:58 PM    <DIR>          $NtUninstallKB828741$
06/07/2004  06:59 PM    <DIR>          $NtUninstallKB833407$
-- More  --

Over the years, people have taken the fact that this pipe is text and written some really powerful utilities to manipulate the text. Years ago, awk and grep arrived, which let you process the text. (awk, for example, could easily take the preceding text and break up the lines into records divided by spaces. grep lets you search through the text for patterns; people still use grep a lot.) Today we have even more powerful languages for text processing, such as Perl.

While simple commands in DOS such as dir use text, more powerful tools work with objects. If you're managing a set of processes on a Windows system, a single process is not just a line of text. A single process is an object made up of more objects such as the ID for the process, the security settings, and so on. These are more than just text.

If a tool retrieves a set of processes and then writes the information out just as text for another tool to use, the second tool is getting cheated. The tool is only getting a text-based form of the processes, not the actual objects.

A better approach, then, is to simply let the first tool pipe the actual objects on to the second tool. At first this might seem like a problem since ultimately we are dealing with a console window which displays text. But if you think about the dir | more commands working together, the only output that finally makes it to the console window is the output from the more. The information passed from dir to more is never displayed on the console.

Of course, eventually the objects do have to be displayed as text in the console window. That's where the ToString method comes in that I mentioned in the previous section. At the end of the pipeline the console can simply call ToString to get a text-representation of the object, and write that text to the console window.

Here's an example:

PS C:\WINDOWS> dir | sort LastWriteTime | more


    Directory: Microsoft.PowerShell.Core\FileSystem::C:\WINDOWS


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---          7/4/1995   1:33 PM      11776 Ckrfresh.exe
-a---         7/31/1995   5:44 PM     212480 PCDLIB32.DLL
-a---          5/3/1996  10:36 AM      18432 Setup_ck.dll
-ar--          5/3/1996  12:21 PM      27648 Setup_ck.exe
-a---         7/22/1998  12:29 AM         21 CS_SETUP.ini
-a---        10/29/1998   3:45 PM     306688 IsUninst.exe
-a---         1/12/1999  10:39 AM       6656 delttsul.exe
-a---         1/12/1999  10:40 AM      29184 rmud.exe
-a---         6/18/1999   4:49 PM     165888 Ckconfig.exe
-a---        11/10/1999   3:05 PM      86016 unvise32qt.exe
...

(dir is an alias for Get-Children, sort is an alias for Sort-Object, and more is a function that calls Out-Host -paging. Thus, dir | sort LastWriteTime | more is shorthand for Get-ChildItem | Sort-Object LastWriteTime | Out-Host -Paging.)

This example lists the directory contents, sorts it on the LastWriteTime field, and then writes it out per page. But since the objects pass through the pipeline and are only converted to text at the end, you can save the results of any output into a variable, preserving the types, like so:

PS C:\WINDOWS> $a = dir | sort LastWriteTime

The variable $a now contains the results of the Sort-Object command, but the contents of $a is not text. The data is preserved. The dir command (Get-ChildItem) returned an array of FileInfo and DirectoryInfo objects, which was piped through the sort (Sort-Object) command. Thus, $a is an array of FileInfo and DirectoryInfo objects. You can access the individual elements using brackets like so:

PS C:\WINDOWS> $a[0]

    Directory: Microsoft.PowerShell.Core\FileSystem::C:\WINDOWS

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---          7/4/1995   1:33 PM      11776 Ckrfresh.exe


PS C:\WINDOWS> $a[1]

    Directory: Microsoft.PowerShell.Core\FileSystem::C:\WINDOWS

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---         7/31/1995   5:44 PM     212480 PCDLIB32.DLL

In the next section I talk about calling methods on objects in more detail but here you can see that if you call GetType on the objects you can see for sure the type of objects you have (and that they definitely aren't text strings). Here I find out the type of my $a variable:

PS C:\WINDOWS> $a.GetType()

IsPublic IsSerial Name                BaseType
-------- -------- ----                --------
True     True     Object[]            System.Array

The $a object is an array. Let's see what the first element is:

PS C:\WINDOWS> $a[0].GetType()

IsPublic IsSerial Name                BaseType
-------- -------- ----                --------
True     True     FileInfo            System.IO.FileSystemInfo

6. Make use of the objects and their members

The objects that pass through the pipe are instances of classes. You can make use of their methods and data.

Don't forget that your objects, including those that are the results of the commands, have members that you can make use of. You can find an object's members using the Get-Member command:

PS C:\Documents and Settings\Jeff> $a = "abc"
PS C:\Documents and Settings\Jeff> Get-Member -InputObject $a


   TypeName: System.String

Name             MemberType            Definition
----             ----------            ----------
Clone            Method                System.Object Clone()
CompareTo        Method                System.Int32 CompareTo(Object value),...
Contains         Method                System.Boolean Contains(String value)
CopyTo           Method                System.Void CopyTo(Int32 sourceIndex,...
EndsWith         Method                System.Boolean EndsWith(String value)...
...

If you pipe the output from one command through Get-Member, Get-Member will list the members for each type of object that comes through. If two objects come through and they are of the same type, Get-Member will only list the members once for that type. For example, if you want to know what members are available of the objects returned by the Get-Process command, pipe the results through Get-Member, like so:

PS C:\Documents and Settings\Jeff> Get-Process | Get-Member


   TypeName: System.Diagnostics.Process

Name                           MemberType     Definition
----                           ----------     ----------
Handles                        AliasProperty  Handles = Handlecount
Name                           AliasProperty  Name = ProcessName
NPM                            AliasProperty  NPM = NonpagedSystemMemorySize
PM                             AliasProperty  PM = PagedMemorySize
VM                             AliasProperty  VM = VirtualMemorySize
WS                             AliasProperty  WS = WorkingSet
add_Disposed                   Method         System.Void add_Disposed(Event...
add_ErrorDataReceived          Method         System.Void add_ErrorDataRecei...
...

The Where-Object command can be used to filter objects in the pipeline, allowing only objects through that meet the criteria you specify. To specify criteria, you will access the members of the objects passing through the pipeline.

For example, although I didn't include the full output here, the Get-Process | Get-Member results showed me the objects in the results of Get-Process each have a member called VirtualMemorySize. Suppose, then, I want to list all processes that have a virtual memory size greater than 100 MB (which is 104,857,600 bytes). Here's how I can do it:

PS C:\Documents and Settings\Jeff> Get-Process | Where-Object {$_.VirtualMemorySize -gt 104857600}

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    948      15    38928      52788   346    12.23    712 dexplore
    256      10    72624      81592   194   136.00   2664 firefox
    350      14    30948      44268   133     7.06   3576 OUTLOOK
    309       8    31928       6404   833   221.14    356 sqlservr
    334       9    22188      14784   817     0.53    852 sqlservr
   1598      91    18668      27404   142   156.75   1864 svchost
    356      24    15708      23740   169   201.47   1536 WINWORD

The Where-Object command takes a parameter that is a script in curly braces. The script runs for each object passing through the pipeline. As each object passes through, it gets placed in the variable called $_. Thus, $_.VirtualMemorySize refers to the VirtualMemorySize member of the object currently in the pipeline. The -gt is the comparison operator for greater than; thus, I'm testing if the current object has a virtual memory size of greater than 104,857,600 bytes.

Get-ChildItem | foreach { $_.Fullname }

As you can see, you should be familiar with the members of the objects, which can be found in the online help. If you go to the online help page, scroll down and click on System, scroll down and click on String, you'll be at the help page for the String class. At the very bottom (as well as in the tree in the left pane) is an item called String Members. That page lists all the members for the String class.

For example, one member is EndsWith. This tells you if a string ends with a given set of characters. You're free to use this member. Here's a quick example:

PS C:\> $a = "This is a test"
PS C:\> $a.EndsWith("test")
True
PS C:\> $a.EndsWith("this")
False
PS C:\>

7. Format your output

You have full control of how the format looks of your output using the various Format- commands.

PowerShell shuttles objects through the pipeline and only obtains the text version of the objects when ready for display, unless told to do otherwise with the commands. If you look at the list of members in the objects that come out of Get-Process, however, you'll see far more than what you see in the output. Which members appear in the output? The answer comes from the system configuration. Inside the Windows PowerShell directory is a file called dotnettypes.format.ps1xml. This is an XML file that describes the formatting to be used in the output of various types. There are actually several such files, each describing different types, each with the filename pattern *.format.ps1xml. Others include powershellcore.format.ps1xml and filesystem.format.ps1xml. You can learn more about these files here.

To format the output, PowerShell users a command called FormatTable. This command takes a pipeline of objects and writes them out using the format defined as the *.format.ps1xml files.

You can, however, override the settings and choose which members appear in the output, like so:

Get-Process | Format-Table Id, Name
  Id Name
  -- ----
2876 alg
 532 ApntEx
2044 Apoint
3448 calc
1824 CFSvcs
2176 cmd
3760 cmd
1640 Crypserv
1316 csrss

This will display the Id and Name columns in the table, regardless of the default settings.

PowerShell has four different Format commands:

You can explore each of these with Get-Help.

8. Remember everything is a hierarchy

PowerShell Drives exist for more than just the file system. You can access the registry, the environment, and other data as a drive.

The idea of a computer being arranged hierarchically has been around a long time. Unix has always treated all the hardware as being part of the hierarchical filesystem, for example. Similarly, PowerShell treats various aspects of the computer as drives. You can see what all drives are currently available with the Get-PSDrive command:

PS C:\Documents and Settings\Jeff> Get-PSDrive

Name       Provider      Root                       CurrentLocation
----       --------      ----                       ---------------
Alias      Alias
C          FileSystem    C:\                        Documents and Settings\Jeff
cert       Certificate   \
D          FileSystem    D:\
Env        Environment
Function   Function
HKCU       Registry      HKEY_CURRENT_USER
HKLM       Registry      HKEY_LOCAL_MACHINE
Variable   Variable

The list of aliases, for example, is a drive. You can switch to that drive as you would any other drive like so:

PS C:\WINDOWS\system32> cd alias:
PS Alias:\>
PS Alias:\> dir

CommandType     Name                  Definition
-----------     ----                  ----------
Alias           ac                    Add-Content
Alias           asnp                  Add-PSSnapin
Alias           clc                   Clear-Content
Alias           cli                   Clear-Item
Alias           clp                   Clear-ItemProperty
Alias           clv                   Clear-Variable

The alias: drive doesn't provide subdirectories. Others, however, such as the Registry, do:

PS Alias:\> cd HKCU:
PS HKCU:\> dir

   Hive: Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER

SKC  VC Name                           Property
---  -- ----                           --------
  2   0 AppEvents                      {}
  1   0 CLSID                          {}
  4  32 Console                        {ColorTable00, ColorTable01, ColorTab...
 24   1 Control Panel                  {Opened}
  0   8 Environment                    {BAKEFILE_PATHS, INCLUDE, LIB, PROMPT...
  0   1 HBA                            {Version}
  1   5 Identities                     {Identity Ordinal, Migrated5, Last Us...
  3   0 Keyboard Layout                {}
  0   0 Movie Magic Screenwriter       {}
 19   0 Movie Magic Screenwriter Vo... {}
  0   0 Network                        {}
  1   1 Note-It                        {(default)}
  4   1 Printers                       {DeviceOld}
  1   1 RemoteAccess                   {InternetProfile}
  1   7 S                              {AutodiscoveryFlags, DetectedInterfac...
100   1 Software                       {(default)}
  0   0 UNICODE Program Groups         {}
  2   0 Windows 3.1 Migration Status   {}
  1   0 Win_32                         {}
  0   1 SessionInformation             {ProgramCount}
  0   7 Volatile Environment           {LOGONSERVER, CLIENTNAME, SESSIONNAME...


PS HKCU:\> cd Software\Microsoft
PS HKCU:\Software\Microsoft> dir


   Hive: Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software\Microso
ft

SKC  VC Name                           Property
---  -- ----                           --------
  1   0 Active Setup                   {}
  3   0 ActiveMovie                    {}
  2   0 Advanced INF Setup             {}
  1   0 ASF Stream Descriptor File     {}
  1   0 Automap                        {}
  1   4 Broadband Networking           {StatusTimeout, InternetStatusTimeout...
  1   1 ClipArt Gallery                {UserAdded}
  0   5 Clipbook                       {WindowsClipBook Viewer, WindowsClipb...
  0   2 Clock                          {iFormat, {CCF5A555-D92E-457b-9235-2B...

Just as you could in DOS, you can use filename patterns:

PS HKCU:\Software\Microsoft> dir *windows*


   Hive: Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software\Microso
ft

SKC  VC Name                           Property
---  -- ----                           --------
  3   0 Windows                        {}
  0   1 Windows Genuine Advantage      {code}
  0   5 Windows Help                   {Maximized, Xl, Xr, Yd...}
  1   0 Windows Media                  {}
  1   0 Windows NT                     {}
  1   0 Windows Script                 {}
  1   0 Windows Script Host            {}

The ? character will match a single character. For example, abc? will match all items starting with abc followed by a single character. You can also use brackets to specify a set of characters to match. For example, abc[123] will match abc1, abc2, and abc3.

9. Use flow controls and scripts

The PowerShell language is powerful and supports scripting.

Many commands take scripts as parameters. The Where-Object command is one example. I showed you this code earlier:

Get-Process | Where-Object {$_.VirtualMemorySize -gt 104857600}

The For-Each object can execute a script for each item in a list. Here's a simple example:

PS C:\WINDOWS\system32> 1..10 | foreach { $_ * 2 }
2
4
6
8
10
12
14
16
18
20

This isn't particularly useful by itself. But the technique is applicable in many places. For example, suppose you want to copy every text file in a directory that has the string #backup in the first line to the c:\backups directory. Here's how you could do it:

dir *.txt | foreach { if ((Get-Content $_ -totalCount 1) -eq "#backup") { copy $_ c:\backups } }

This line starts with dir (or Get-ChildItem) and pipes the results through the foreach command. The foreach command runs the script shown in curly braces for each file encountered. The script reads the first line of each file (via Get-Content, passing -totalCount 1 to get only the first line), and compares it to the string #backup. If the string matches, the script copies the file.

10. Customize your environment

You can customize your environment by saving functions and changes in the profile.ps1 files.

When you develop your own scripts to do the work you need, you'll probably want to save them for later. You might also want to create more aliases of the commands you find userful. For example, in the previous section I created a script that will back up text files that start with #backup.

You can put your customizations in your own profile. The previous script would need to be a function such as this:

function Backup-TextFiles {
    dir *.txt | foreach {
        if ((Get-Content $_ -totalCount 1) -eq "#backup") { 
            copy $_ c:\backups } 
        }
    }

This function would then go in your profile. The profile is a file called Profile.ps1, and it goes in your home directory, which is \Documents and Settings\<username>\My Documents\WindowsPowerShell\profile.ps1 where <username> is replaced by your actual username.

However, before you can run the script, it must be signed for security. (Alternatively, you can turn off the security restrictions. Several blogs are saying to do this, but that's a really bad idea.) This is a great link that explains how you can sign your script. (In the article, the author mentions adding a snap-in to MMC. To get there in mmc.exe, click File->Add/Remove Snap-in. Make sure when you run the makecert.exe utility, you're in the directory that the author mentions contains makecert.exe. Also, I had to refresh the MMC window before my changes appeared.)

Conclusion

The new Windows PowerShell is big, but don't be overwhelmed by it. When I first started using it, I couldn't imagine how it could make my life easier. There were too many unfamiliar commands and the commands seemed very long to type. But in a short time, I started to learn the commands and aliases, and understand the whole way of thinking in the PowerShell world. I'm glad Microsoft has finally given us this tool and excited to see what new tools the community will build around PowerShell to help make our lives easier. Enjoy!

Jeff Cogswell lives near Cincinnati and has been working as a software engineer for over 15 years.


Return to the Windows DevCenter.

Copyright © 2009 O'Reilly Media, Inc.