Skip to main content

Windows

Visit Each Element of an Array

Problem

You want to work with each element of an array.

Solution

To access each item in an array onebyone, use the ForeachObject cmdlet:

PS >$myArray = 1,2,3 PS >$sum = 0 PS >$myArray | ForeachObject { $sum += $_ } PS >$sum 6

To access each item in an array in a more scriptlike fashion, use the foreach scripting keyword:

PS >$myArray = 1,2,3 PS >$sum = 0 PS >foreach($element in $myArray) { $sum += $element } PS >$sum 6

To access items in an array by position, use a for loop:

PS >$myArray = 1,2,3 PS >$sum = 0 PS >for($counter = 0; $counter lt $myArray.Count; $counter++) { >> $sum += $myArray[$counter] >> } >> PS >$sum 6

Discussion

PowerShell provides three main alternatives to working with elements in an array. The ForeachObject cmdlet and foreach scripting keyword techniques visit the items in an array one element at a time, while the for loop (and related looping constructs) lets you work with the items in an array in a less structured way.

Program: Add Extended File Properties to Files in PowerShell

Discussion

The Explorer shell provides useful information about a file when you click on its Properties dialog. It includes the authoring information, image information, music information, and more.

PowerShell doesn’t expose this information by default, but it is possible to obtain these properties from the Shell.Application COM object. Example 175 does just that—and adds this extended information as properties to the files returned by the GetChildItem cmdlet.

Example 175. AddExtendedFileProperties.ps1

############################################################################## ## ## AddExtendedFileProperties.ps1 ## ## Add the extended file properties normally shown in Explorer's ## "File Properties" tab. ## ## ie: ## ## PS >GetChildItem | AddExtendedFileProperties.ps1 | ## FormatTable Name,"Bit Rate" ## ##############################################################################

begin

{ ## Create the Shell.Application COM object that provides this ## functionality $shellObject = NewObject Com Shell.Application

## Store the property names and identifiers for all of the shell ## properties $itemProperties = @{

1 = "Size"; 2 = "Type"; 3 = "Date Modified"; 4 = "Date Created"; 5 = "Date Accessed"; 7 = "Status"; 8 = "Owner"; 9 = "Author"; 10 = "Title"; 11 = "Subject"; 12 = "Category"; 13 = "Pages"; 14 = "Comments"; 15 = "Copyright"; 16 = "Artist"; 17 = "Album Title"; 19 = "Track Number"; 20 = "Genre"; 21 = "Duration"; 22 = "Bit Rate"; 23 = "Protected"; 24 = "Camera Model"; 25 = "Date Picture Taken"; 26 = "Dimensions"; 30 = "Company"; 31 = "Description"; 32 = "File Version"; 33 = "Product Name"; 34 = "Product Version" }

}

process

{ ## Get the file from the input pipeline. If it is just a filename ## (rather than a real file,) piping it to the GetItem cmdlet will ## get the file it represents. $fileItem = $_ | GetItem

## Don't process directories if($fileItem.PsIsContainer) {

Example 175. AddExtendedFileProperties.ps1 (continued)

$fileItem return }

## Extract the file name and directory name $directoryName = $fileItem.DirectoryName $filename = $fileItem.Name

## Create the folder object and shell item from the COM object $folderObject = $shellObject.NameSpace($directoryName) $item = $folderObject.ParseName($filename)

## Now, go through each property and add its information as a ## property to the file we are about to return foreach($itemProperty in $itemProperties.Keys) {

$fileItem | AddMember NoteProperty $itemProperties[$itemProperty] ` $folderObject.GetDetailsOf($item, $itemProperty) }

## Finally, return the file with the extra shell information $fileItem }

List All IP Addresses for a Computer

Problem

You want to list all IP addresses for a computer.

Solution

To list IP addresses assigned to a computer, use the ipconfig application: PS >ipconfig

Discussion

The standard ipconfig application works well to manage network configuration options on a local machine. To view IP addresses on a remote computer, you have two options.

Use the Win32_NetworkAdapterConfiguration WMI class

To view IP addresses a remote computer, use the Win32_NetworkAdapterConfiguration WMI class. Since that lists all network adapters, use the WhereObject cmdlet to restrict the results to those with an IP address assigned to them:

PS >GetWmiObject Win32_NetworkAdapterConfiguration –Computer LEEDESK | >> WhereObject { $_.IpEnabled } >>

DHCPEnabled : True IPAddress : {192.168.1.100} DefaultIPGateway : {192.168.1.1}

DNSDomain
: hsd1.wa.comcast.net.

ServiceName
: USB_RNDIS

Description
: Linksys WirelessG USB Network Adapter with SpeedBooste

r v2 Packet Scheduler Miniport

Index
: 13

Run ipconfig on the remote computer

PS >InvokeRemoteExpression \\LEEDESK { ipconfig }

Download a File from the Internet

Problem

You want to download a file from a web site on the Internet.

Solution

Use the DownloadFile() method from the .NET Framework’s System.Net.WebClient class to download a file:

PS >$source = "http://www.leeholmes.com/favicon.ico" PS >$destination = "c:\temp\favicon.ico" PS > PS >$wc = NewObject System.Net.WebClient PS >$wc.DownloadFile($source, $destination)

Discussion

The System.Net.WebClient class from the .NET Framework lets you easily upload and download data from remote web servers.

The WebClient class acts much like a web browser, in that you can specify a user agent, proxy (if your outgoing connection requires one), and even credentials.

All web browsers send a user agent identifier along with their web request. This identifier tells the web site what application is making the request—such as Internet Explorer, Firefox, or an automated crawler from a search engine. Many web sites check this user agent identifier to determine how to display the page. Unfortunately, many fail entirely if they can’t determine the user agent for the incoming request. To make the System.Net.WebClient identify itself as Internet Explorer, use the following commands, instead:

$userAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2;)" $wc = NewObject System.Net.WebClient $wc.Headers.Add("useragent", $userAgent)

Notice that the solution uses a fully qualified path for the destination file. This is an important step, as the DownloadFile() method saves its files to the directory in which PowerShell.exe started (the root of your user profile directory by default) otherwise.

You can use the DownloadFile() method to download web pages just as easily as you download files—you need to supply only an URL as a source (such as http://blogs. msdn.com/powershell/rss.xml ) instead of a filename. If you ultimately intend to parse or read through the downloaded page, the DownloadString() method may be more appropriate.

Find All Files Modified Before a Certain Date

Problem

You want to find all files last modified before a certain date.

Solution

To find all files modified before a certain date, use the GetChildItem cmdlet to list the files in a directory, and then use the WhereObject cmdlet to compare the LastWriteTime property to the date you are interested in. For example, to find all files created before this year:

GetChildItem Recurse | WhereObject { $_.LastWriteTime lt "01/01/2007" }

Discussion

Acommon reason to compare files against a certain date is to find recently modified (or not recently modified) files. This looks almost the same as the example given by the solution, but your script can’t know the exact date to compare against.

In this case, the AddDays() method in the .NET Framework’s DateTime class gives you a way to perform some simple calendar arithmetic. If you have a DateTime object, you can add or subtract time from it to represent a different date altogether. For example, to find all files modified in the last 30 days:

$compareDate = (GetDate).AddDays(30) GetChildItem Recurse | WhereObject { $_.LastWriteTime ge $compareDate }

Similarly, to find all files more than 30 days old:

$compareDate = (GetDate).AddDays(30)

GetChildItem Recurse | WhereObject { $_.LastWriteTime lt $compareDate } In this example, the GetDate cmdlet returns an object that represents the current date and time. You call the AddDays() method to subtract 30 days from that time, which stores the date representing “30 days ago” in the $compareDate variable. Next, you compare that date against the LastWriteTime property of each file that the GetChildItem cmdlet returns.

The DateTime class is the administrator’s favorite calendar!

PS >[DateTime]::IsLeapYear(2008)

True

PS >$daysTillSummer = [DateTime] "06/21/2008" (GetDate)

PS >$daysTillSummer.Days

283

For more information about the GetChildItem cmdlet, type GetHelp GetChildItem. 

Get and List the Properties of a Computer Account

Problem

You want to get and list the properties of a specific computer account.

Solution

To list the properties of a computer account, use the [adsi] type shortcut to bind to the computer in Active Directory, and then pass the computer to the FormatList cmdlet:

$computer = [adsi] "LDAP://localhost:389/cn=kenmyer_laptop,ou=West,ou=Sales,dc=Fabrikam,dc=COM"

$computer | FormatList *

Discussion

The solution retrieves the kenmyer_laptop computer from the Sales West OU. By default, the FormatList cmdlet shows only the distinguished name of the computer, so we type FormatList * to display all properties.

If you know the property for which you want the value, specify it by name:

PS >$computer.OperatingSystem Windows Server 2003

Unlike users, some types of Active Directory objects don’t allow you to retrieve their properties by name this way. Instead, you must call the Get() method to retrieve specific properties:

PS >$user.Get("operatingSystem") Windows Server 2003

Measure Statistical Properties of a List in Windows PowerShell

Problem

You want to measure the numeric (minimum, maximum, sum, average) or textual (characters, words, lines) features of a list of objects.

Solution

Use the MeasureObject cmdlet to measure these statistical properties of a list. To measure the numeric features of a stream of objects, pipe those objects to the MeasureObject cmdlet: PS >1..10 | MeasureObject –Average Sum

Count : 10 Average : 5.5 Sum : 55 Maximum : Minimum : Property :

To measure the numeric features of a specific property in a stream of objects, supply that property name to the –Property parameter of the MeasureObject cmdlet. For example, in a directory with files:

PS >GetChildItem | MeasureObject Property Length Max Min Average Sum

Count : 427 Average : 10617025.4918033 Sum : 4533469885 Maximum : 647129088 Minimum : 0 Property : Length

To measure the textual features of a stream of objects, use the –Character, Word, and –Line parameters of the MeasureObject cmdlet:

PS >GetChildItem > output.txt PS >GetContent output.txt | MeasureObject Character Word Line

Lines
Words
Characters Property

964
6083
33484

Discussion

By default, the MeasureObject cmdlet counts only the number of objects it receives. If you want to measure additional properties (such as the maximum, minimum, average, sum, characters, words, or lines) of those objects, then you need to specify them as options to the cmdlet.

For the numeric properties, though, you usually don’t want to measure the objects themselves. Instead, you probably want to measure a specific property from the list—such as the Length property of a file. For that purpose, the MeasureObject cmdlet supports the –Property parameter to which you provide the property you want to measure.

Sometimes, you might want to measure a property that isn’t a simple number—such as the LastWriteTime property of a file. Since the LastWriteTime property is a DateTime, you can’t determine its average immediately. However, if any property allows you to convert it to a number and back in a meaningful way (such as the Ticks property of a DateTime), then you can still compute its statistical properties. Example 62 shows how to get the average LastWriteTime from a list of files.

Example 62. Using the Ticks property of the DateTime class to determine the average LastWriteTime of a list of files

PS >## Get the LastWriteTime from each file PS >$times = dir | ForeachObject { $_.LastWriteTime }

PS >## Measure the average Ticks property of those LastWriteTime

Example 62. Using the Ticks property of the DateTime class to determine the average LastWriteTime of a list of files (continued)

PS >$results = $times | MeasureObject Ticks Average

PS >## Create a new DateTime out of the average Ticks PS >NewObject DateTime $results.Average

Sunday, June 11, 2006 6:45:01 AM

For more information about the MeasureObject cmdlet, type GetHelp MeasureObject.

Program: Add Inline C# to Your PowerShell Script

One of the natural languages to explore after learning PowerShell is C#. It uses many of the same programming techniques as PowerShell and uses the same classes and methods in the .NET Framework as PowerShell does, too. In addition, C# sometimes offers language features or performance benefits not available through PowerShell.

Rather than having to move to C# completely for these situations, Example 158 lets you write and invoke C# directly in your script.

Example 158. InvokeInline.ps1

################################################################################

## InvokeInline.ps1

## Library support for inline C#

##

## Usage

## 1) Define just the body of a C# method, and store it in a string. "Here

## strings" work great for this. The code can be simple:

##

## $codeToRun = "Console.WriteLine(Math.Sqrt(337));"

##

## or more complex:

##

## $codeToRun = @"

## string firstArg = (string) ((System.Collections.ArrayList) arg)[0];

## int secondArg = (int) ((System.Collections.ArrayList) arg)[1];

##

## Console.WriteLine("Hello {0} {1}", firstArg, secondArg );

##

## returnValue = secondArg * 3;

## "@

##

## 2) (Optionally) Pack any arguments to your function into a single object.

## This single object should be stronglytyped, so that PowerShell does

## not treat it as a PsObject.

## An ArrayList works great for multiple elements. If you have only one

## argument, you can pass it directly.

##

## [System.Collections.ArrayList] $arguments =

## NewObject System.Collections.ArrayList

## [void] $arguments.Add("World")

## [void] $arguments.Add(337)

##

## 3) Invoke the inline code, optionally retrieving the return value. You can

## set the return value in your inline code by assigning it to the

## "returnValue" variable as shown above.

##

## $result = InvokeInline $codeToRun $arguments

##

##

## If your code is simple enough, you can even do this entirely inline:

##

## InvokeInline "Console.WriteLine(Math.Pow(337,2));"

##

################################################################################

param( [string] $code = $(throw "Please specify the code to invoke"), [object] $arg, [string[]] $reference = @() )

## Stores a cache of generated inline objects. If this library is dotsourced ## from a script, these objects go away when the script exits.

Example 158. InvokeInline.ps1 (continued)

if(not (TestPath Variable:\Lee.Holmes.inlineCache)) { ${GLOBAL:Lee.Holmes.inlineCache} = @{} }

## The main function to execute inline C#. ## Pass the argument to the function as a stronglytyped variable. They will ## be available from C# code as the Object variable, "arg". ## Any values assigned to the "returnValue" object by the C# code will be ## returned to the caller as a return value.

function main

{ ## See if the code has already been compiled and cached $cachedObject = ${Lee.Holmes.inlineCache}[$code]

## The code has not been compiled or cached if($cachedObject eq $null) {

$codeToCompile = @" using System;

public class InlineRunner

{ public Object Invoke(Object arg) {

Object returnValue = null;

$code

return returnValue; } } "@

## Obtains an ICodeCompiler from a CodeDomProvider class. $provider = NewObject Microsoft.CSharp.CSharpCodeProvider

## Get the location for System.Management.Automation DLL $dllName = [PsObject].Assembly.Location

## Configure the compiler parameters $compilerParameters = NewObject System.CodeDom.Compiler.CompilerParameters

$assemblies = @("System.dll", $dllName) $compilerParameters.ReferencedAssemblies.AddRange($assemblies) $compilerParameters.ReferencedAssemblies.AddRange($reference) $compilerParameters.IncludeDebugInformation = $true $compilerParameters.GenerateInMemory = $true

## Invokes compilation.

Example 158. InvokeInline.ps1 (continued)

$compilerResults = $provider.CompileAssemblyFromSource($compilerParameters, $codeToCompile)

## Write any errors if generated. if($compilerResults.Errors.Count gt 0) {

$errorLines = "`n$codeToCompile" foreach($error in $compilerResults.Errors) {

$errorLines += "`n`t" + $error.Line + ":`t" + $error.ErrorText } WriteError $errorLines

} ## There were no errors. Store the resulting object in the object " ## cache. else {

${Lee.Holmes.inlineCache}[$code] = $compilerResults.CompiledAssembly.CreateInstance("InlineRunner") }

$cachedObject = ${Lee.Holmes.inlineCache}[$code] }

## Finally invoke the C# code if($cachedObject ne $null) {

return $cachedObject.Invoke($arg) } }

. Main

Get the Children of an Active Directory Container in PowerShell

Problem

You want to list all the children of an Active Directory container.

Solution

To list the items in a container, use the [adsi] type shortcut to bind to the OU in Active Directory, and then access the PsBase.Children property of that container:

$sales =

[adsi] "LDAP://localhost:389/ou=Sales,dc=Fabrikam,dc=COM"

$sales.PsBase.Children

Discussion

The solution lists all the children of the Sales OU. This is the level of information you typically get from selecting a node in the ADSIEdit MMC snapin. If you want to filter this information to include only users, other organizational units, or more complex queries.

Make Decisions with Comparison and Logical Operators in Windows PowerShell

Problem

You want to compare some data with other data and make a decision based on that comparison.

Solution

Use PowerShell’s logical operators to compare pieces of data and make decisions based on them.

Comparison operators:

eq, ne, ge, gt, lt, le, like, notlike, match, notmatch, contains, notcontains, is, isnot

Logical operators:

and, or, xor, not

Discussion

PowerShell’s logical and comparison operators let you compare pieces of data, or test data for some condition. An operator either compares two pieces of data (a binary operator) or tests one piece of data (a unary operator). All comparison operators are binary operators (they compare two pieces of data), as are most of the logical operators. The only unary logical operator is the not operator, which returns the true/ false opposite of the data that it tests.

Comparison operators compare two pieces of data and return a result that depends on the specific comparison operator. For example, you might want to check whether a collection has at least a certain number of elements:

PS >(dir).Count ge 4 True

or, check whether a string matches a given regular expression:

PS >"Hello World" match "H.*World" True

Most comparison operators also adapt to the type of their input. For example, when you apply them to simple data such as a string, the like and match comparison operators determine whether the string matches the specified pattern. When you apply them to a collection of simple data, those same comparison operators return all elements in that collection that match the pattern you provide.

The match operator takes a regular expression as its argument. One of the more common regular expression symbols is the $ character, which represents the end of line. The $ character also represents the

start of a PowerShell variable, though! To prevent PowerShell from interpreting characters as language terms or escape sequences, place the string in single quotes rather than double quotes:

PS >"Hello World" match "Hello" True PS >"Hello World" match 'Hello$' False

Logical operators combine true or false statements and return a result that depends on the specific logical operator. For example, you might want to check whether a string matches the wildcard pattern you supply, and that it is longer than a certain number of characters:

PS >$data = "Hello World" PS >($data like "*llo W*") and ($data.Length gt 10) True PS >($data like "*llo W*") and ($data.Length gt 20) False

Some of the comparison operators actually incorporate aspects of the logical operators. Since using the opposite of a comparison (such as like) is so common, PowerShell provides comparison operators (such as notlike) that save you from having to use the not operator explicitly.

Comparison operators and logical operators (when combined with flow control statements) form the core of how we write a script or command that adapts to its data and input.

For more information about PowerShell’s operators, type GetHelp About_Operator.