Skip to main content

Windows

Manage Scheduled Tasks on a Computer

Problem

You want to schedule a task on a computer.

Solution

To manage scheduled tasks, use the schtasks.exe application. To view the list of scheduled tasks:

PS >schtasks

TaskName Next Run Time Status ==================================== ======================== ============= Defrag C 03:00:00, 5/21/2007 User_Feed_Synchronization{CA4D6D9C 18:34:00, 5/20/2007 User_Feed_Synchronization{CA4D6D9C 18:34:00, 5/20/2007

To schedule a task to defragment C: every day at 3:00 a.m.:

schtasks /create /tn "Defrag C" /sc DAILY ` /st 03:00:00 /tr "defrag c:" /ru Administrator

To remove a scheduled task by name:

schtasks /delete /tn "Defrag C"

Discussion

The example in the solution tells the system to defragment C: every day at 3:00 a.m.. It runs this command under the Administrator account, since the defrag.exe command requires administrative privileges. In addition to scheduling tasks on the local computer, the schtasks.exe application also allows you to schedule tasks on remote computers.

On Windows Vista, the schtasks.exe application has been enhanced to support event triggers, conditions, and additional settings.

For more information about the schtasks.exe application, type schtasks /?.

How to Search and Replace Text in a File in Windows PowerShell

Problem

You want to search for text in a file and replace that text with something new.

Solution

To search and replace text in a file, first store the content of the file in a variable, and then store the replaced text back in that file as shown in Example 74.

Example 74. Replacing text in a file

PS >$filename = "file.txt" PS >$match = "source text" PS >$replacement = "replacement text" PS > PS >$content = GetContent $filename PS >$content This is some source text that we want to replace. One of the things you may need to be careful careful about with Source Text is when it spans multiple lines, and may have different Source Text capitalization. PS > PS >$content = $content creplace $match,$replacement PS >$content This is some replacement text that we want to replace. One of the things you may need to be careful careful about with Source Text is when it spans multiple lines, and may have different Source Text capitalization. PS >$content | SetContent $filename

Discussion

Using PowerShell to search and replace text in a file (or many files!) is one of the best examples of using a tool to automate a repetitive task. What could literally take months by hand can be shortened to a few minutes (or hours, at most).

Notice that the solution uses the –creplace operator to replace text in a casesensitive manner. This is almost always what you will want to do, as the replacement text uses the exact capitalization that you pro

vide. If the text you want to replace is capitalized in several different ways (as in the term “Source Text” from the solution), then search and replace several times with the different possible capitalizations.

Example 74 illustrates what is perhaps the simplest (but actually most common) scenario:

  • You work with an ASCII text file.
  • You replace some literal text with a literal text replacement.
  • You don’t worry that the text match might span multiple lines.
  • Your text file is relatively small.

If some of those assumptions don’t hold true, then this discussion shows you how to tailor the way you search and replace within this file.

Work with files encoded in Unicode or another (OEM) code page

By default, the SetContent cmdlet assumes that you want the output file to contain plain ASCII text. If you work with a file in another encoding (for example, Unicode or an OEM code page such as Cyrillic), use the –Encoding parameter of the OutFile cmdlet to specify that:

$content | OutFile –Encoding Unicode $filename $content | OutFile –Encoding OEM $filename

Replace text using a pattern instead of plain text

Although it is most common to replace one literal string with another literal string, you might want to replace text according to a pattern in some advanced scenarios. One example might be swapping first name and last name. PowerShell supports this type of replacement through its support of regular expressions in its replacement operator:

PS >$content = GetContent names.txt PS >$content John Doe Mary Smith PS >$content replace '(.*) (.*)','$2, $1' Doe, John Smith, Mary

Replace text that spans multiple lines

The GetContent cmdlet used in the solution retrieves a list of lines from the file. When you use the –replace operator against this array, it replaces your text in each of those lines individually. If your match spans multiple lines, as shown between lines 3 and 4 in Example 74, the –replace operator will be unaware of the match and will not perform the replacement.

If you want to replace text that spans multiple lines, then it becomes necessary to stop treating the input text as a collection of lines. Once you stop treating the input as a collection of lines, it is also important to use a replacement expression that can ignore line breaks, as shown in Example 75.

Example 75. Replacing text across multiple lines in a file

$filename = GetItem file.txt $singleLine = [System.IO.File]::ReadAllText($filename.FullName) $content = $singleLine creplace "(?s)Source(\s*)Text",'Replacement$1Text'

The first and second lines of Example 75 read the entire content of the file as a sin gle string. It does this by calling the [System.IO.File]::ReadAllText() method from the .NET Framework, since the GetContent cmdlet splits the content of the file into individual lines.

The third line of this solution replaces the text by using a regular expression pattern. The section, Source(\s*)Text, scans for the word Source followed optionally by some whitespace, followed by the word Text. Since the whitespace portion of the regular expression has parentheses around it, we want to remember exactly what that whitespace was. By default, regular expressions do not let newline characters count as whitespace, so the first portion of the regular expression uses the singleline option (?s) to allow newline characters to count as whitespace. The replacement portion of the –replace operator replaces that match with Replacement, followed by the exact whitespace from the match that we captured ($1), followed by Text.

Replace text in large files

The approaches used so far store the entire contents of the file in memory as they replace the text in them. Once we’ve made the replacements in memory, we write the updated content back to disk. This works well when replacing text in small, medium, and even moderately large files. For extremely large files (for example, more than several hundred megabytes), using this much memory may burden your system and slow down your script. To solve that problem, you can work on the files linebyline, rather than with the entire file at once.

Since you’re working with the file linebyline, it will still be in use when you try to write replacement text back into it. You can avoid this problem if you write the replacement text into a temporary file until you’ve finished working with the main file. Once you’ve finished scanning through our file, you can delete it and replace it with the temporary file.

$filename = "file.txt" $temporaryFile = [System.IO.Path]::GetTempFileName()

$match = "source text" $replacement = "replacement text"

GetContent $filename | ForeachObject { $_ creplace $match,$replacement | AddContent $temporaryFile }

RemoveItem $filename MoveItem $temporaryFile $filename

Verify the Digital Signature of a PowerShell Script

Problem

You want to verify the digital signature of a PowerShell script or formatting file.

Solution

To validate the signature of a script or formatting file, use the GetAuthenticodeSignature cmdlet:

PS >GetAuthenticodeSignature .\test.ps1

Directory: C:\temp

SignerCertificate Status Path

FD48FAA9281A657DBD089B5A008FAFE61D3B32FD Valid test.ps1

Discussion

The GetAuthenticodeSignature cmdlet gets the Authenticode signature from a file. This can be a PowerShell script or formatting file, but the cmdlet also supports DLLs and other Windows standard executable file types.

By default, PowerShell displays the signature in a format that summarizes the certificate and its status. For more information about the signature, use the FormatList cmdlet, as shown in Example 162.

Example 162. PowerShell displaying detailed information about an Authenticode signature

PS >GetAuthenticodeSignature .\test.ps1 | FormatList

SignerCertificate : [Subject] CN=PowerShell User

[Issuer] CN=PowerShell Local Certificate Root

[Serial Number] 454D75B8A18FBDB445D8FCEC4942085C

[Not Before] 4/22/2007 12:32:37 AM

[Not After] 12/31/2039 3:59:59 PM

[Thumbprint] FD48FAA9281A657DBD089B5A008FAFE61D3B32FD

TimeStamperCertificate : Status : Valid StatusMessage : Signature verified. Path : C:\temp\test.ps1

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

Find the Owner of a Group in Windows PowerShell

Problem

You want to get the owner of a security or distribution group.

Solution

To determine the owner of a group, use the [adsi] type shortcut to bind to the group in Active Directory, and then retrieve the ManagedBy property: $group = [adsi] "LDAP://localhost:389/cn=Management,ou=West,ou=Sales,dc=Fabrikam,dc=COM"

$group.ManagedBy

Discussion

The solution retrieves the owner of the Management group from the Sales West OU. To do this, it accesses the ManagedBy property of that group. This property exists only when populated by the administrator group, but it is a best practice to do so.

Unlike groups, 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 >$group.Get("name") Management

Search a String for Text or a Pattern in Windows PowerShell

Problem

You want to determine if a string contains another string, or want to find the position of a string within another string.

Solution

PowerShell provides several options to help you search a string for text.

Use the –like operator to determine whether a string matches a given DOSlike wildcard:

PS >"Hello World" –like "*llo W*"

True Use the –match operator to determine whether a string matches a given regular expression:

PS >"Hello World" –match '.*l[lz]o W.*$' True

Use the Contains() method to determine whether a string contains a specific string:

PS >"Hello World".Contains("World")

True

Use the IndexOf() method to determine the location of one string within another:

PS >"Hello World".IndexOf("World")

6

Discussion

Since PowerShell strings are fully featured .NET objects, they support many stringoriented operations directly. The Contains() and IndexOf() methods are two examples of the many features that the String class supports.

Although they use similar characters, simple wildcards and regular expressions serve significantly different purposes. Wildcards are much more simple than regular expressions, and because of that, more constrained. While you can summarize the rules for wildcards in just four bullet points, entire books have been written to help teach and illuminate the use of regular expressions.

Acommon use of regular expressions is to search for a string that spans multiple lines. By default, regular expressions do not search across lines, but you can use the singleline (?s) option to instruct them

to do so:

PS >"Hello `n World" match "Hello.*World" False PS >"Hello `n World" match "(?s)Hello.*World" True

Wildcards lend themselves to simple matches, while regular expressions lend themselves to more complex matches.

One difficulty sometimes arises when you try to store the result of a PowerShell command in a string, as shown in Example 53.

Example 53. Attempting to store output of a PowerShell command in a string

PS >GetHelp GetChildItem

NAME GetChildItem

SYNOPSIS Gets the items and child items in one or more specified locations.

(...)

PS >$helpContent = GetHelp GetChildItem PS >$helpContent match "location" False

The –match operator searches a string for the pattern you specify but seems to fail in this case. This is because all PowerShell commands generate objects. If you don’t store that output in another variable or pass it to another command, PowerShell converts to a text representation before it displays it to you. In Example 53, $helpContent is a fully featured object, not just its string representation:

PS >$helpContent.Name

GetChildItem

To work with the textbased representation of a PowerShell command, you can explicitly send it through the OutString cmdlet. The OutString cmdlet converts its input into the textbased form you are used to seeing on the screen:

PS >$helpContent = GetHelp GetChildItem | OutString

PS >$helpContent match "location"

True

Program: Determine Properties Available to WMI Filters

When you want to access a specific WMI instance with PowerShell’s [Wmi] type shortcut, you might at first struggle to determine what properties WMI lets you search on. These properties are called key properties on the class. Example 151 gets all the properties you may use in a WMI filter for a given class.

Example 151. GetWmiClassKeyProperty.ps1

############################################################################## ## ## GetWmiClassKeyProperty.ps1 ## ## Get all of the properties that you may use in a WMI filter for a given class. ## ## ie: ## ## PS >GetWmiClassKeyProperty Win32_Process ## ##############################################################################

param( [WmiClass] $wmiClass )

## WMI classes have properties foreach($currentProperty in $wmiClass.PsBase.Properties) {

## WMI properties have qualifiers to explain more about them foreach($qualifier in $currentProperty.Qualifiers) {

## If it has a 'Key' qualifier, then you may use it in a filter if($qualifier.Name eq "Key") {

$currentProperty.Name } } }

List All Running PowerShell Services

Problem

You want to see which services are running on the system.

Solution

To list all running services, use the GetService cmdlet: PS >GetService

Status
Name
DisplayName

Running
ADAM_Test
Test

Stopped
Alerter
Alerter

Running
ALG
Application Layer Gateway Service

Stopped
AppMgmt
Application Management

Stopped
aspnet_state
ASP.NET State Service

Running
AudioSrv
Windows Audio

Running
BITS
Background Intelligent Transfer Ser...

Running
Browser
Computer Browser

(...)
 

Discussion

The GetService cmdlet retrieves information about all services running on the system. Because these are rich .NET objects (of the type System.ServiceProcess. ServiceController), you can apply advanced filters and operations to make managing services straightforward.

For example, to find all running services:

PS >GetService | WhereObject { $_.Status eq "Running" }

Status Name DisplayName

Running ADAM_Test Test Running ALG Application Layer Gateway Service Running AudioSrv Windows Audio Running BITS Background Intelligent Transfer Ser... Running Browser Computer Browser Running COMSysApp COM+ System Application Running CryptSvc Cryptographic Services

Or, to sort services by the number of services that depend on them:

PS >GetService | SortObject Descending { $_.DependentServices.Count }

Status Name DisplayName

Running RpcSs Remote Procedure Call (RPC) Running PlugPlay Plug and Play Running lanmanworkstation Workstation Running SSDPSRV SSDP Discovery Service Running TapiSrv Telephony (...)

Since PowerShell returns fullfidelity .NET objects that represent system services, these tasks and more become incredibly simple due to the rich amount of information that PowerShell returns for each service. For more information about the GetService cmdlet, type GetHelp GetService.

The GetService cmdlet displays most (but not all) information about running services. For additional information (such as the service’s startup mode), use the GetWmiObject cmdlet:

$service = GetWmiObject Win32_Service | WhereObject { $_.Name eq "AudioSrv" } $service.StartMode

Create an Instance of a .NET Object

Problem

You want to create an instance of a .NET object to interact with its methods and properties.

Solution

Use the NewObject cmdlet to create an instance of an object.

To create an instance of an object using its default constructor, use the NewObject cmdlet with the class name as its only parameter:

PS >$generator = NewObject System.Random PS >$generator.NextDouble() 0.853699042859347

To create an instance of an object that takes parameters for its constructor, supply those parameters to the NewObject cmdlet. In some instances, the class may exist in a separate library not loaded in PowerShell by default, such as the System.Windows. Forms assembly. In that case, you must first load the assembly that contains the class:

[Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") $image = NewObject System.Drawing.Bitmap source.gif $image.Save("source_converted.jpg","JPEG")

To create an object and use it at the same time (without saving it for later), wrap the call to NewObject in parentheses:

PS >(NewObject Net.WebClient).DownloadString("http://live.com")

Discussion

Many cmdlets (such as GetProcess and GetChildItem) generate live .NET objects that represent tangible processes, files, and directories. However, PowerShell supports much more of the .NET Framework than just the objects that its cmdlets produce.

These additional areas of the .NET Framework supply a huge amount of functionality that you can use in your scripts and general system administration tasks.

When it comes to using most of these classes, the first step is often to create an instance of the class, store that instance in a variable, and then work with the methods and properties on that instance. To create an instance of a class, you use the NewObject cmdlet. The first parameter to the NewObject cmdlet is the type name, and the second parameter is the list of arguments to the constructor, if it takes any. The NewObject cmdlet supports PowerShell’s type shortcuts, so you never have to use the fully qualified type name.

Since the second parameter to the NewObject cmdlet is an array of parameters to the type’s constructor, you might encounter difficulty when trying to specify a parameter that itself is a list. Assuming $byte is an array of bytes:

PS >$memoryStream = NewObject System.IO.MemoryStream $bytes NewObject : Cannot find an overload for ".ctor" and the argument count: "11". At line:1 char:27

+ $memoryStream = NewObject System.IO.MemoryStream $bytes

To solve this, provide an array that contains an array:

PS >$parameters = ,$bytes PS >$memoryStream = NewObject System.IO.MemoryStream $parameters

or

PS >$memoryStream = NewObject System.IO.MemoryStream @(,$bytes)

Load types from another assembly

PowerShell makes most common types available by default. However, many are available only after you load the library (called the assembly) that defines them. The MSDN documentation for a class includes the assembly that defines it.

To load an assembly, use the methods provided by the System.Reflection.Assembly class:

PS >[Reflection.Assembly]::LoadWithPartialName("System.Web")

GAC
Version
Location

True

v2.0.50727
C:\WINDOWS\assembly\GAC_32\(…)\System.Web.dll

PS >[Web.HttpUtility]::UrlEncode("http://search.msn.com") http%3a%2f%2fsearch.msn.com

The LoadWithPartialName method is unsuitable for scripts that you want to share with others or use in a production environment. It loads the most current version of the assembly, which may not be the same

as the version you used to develop your script. To load an assembly in the safest way possible, use its fully qualified name with the [Reflection.Assembly]::Load() method.

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

Debug a Script in Windows PowerShell

Problem

You want to diagnose failures or unexpected behavior in a script interactively.

Solution

To generate debugging statements from your script, Use the WriteDebug cmdlet. If you want to step through a region carefully, surround it with SetPsDebug –Step calls. To explore the environment at a specific point of execution, add a line that calls $host.EnterNestedPrompt().

Discussion

By default, PowerShell allows you to assign data to variables you haven’t yet created (thereby creating those variables). It also allows you to retrieve data from variables that don’t exist—which usually happens by accident and almost always causes bugs. To help save you from getting stung by this problem, PowerShell provides a strict mode that generates an error if you attempt to access a nonexisting variable. Example 132 demonstrates this mode.

Example 132. PowerShell operating in strict mode

PS >$testVariable = "Hello" PS >$tsetVariable += " World" PS >$testVariable Hello PS >RemoveItem Variable:\tsetvariable PS >SetPsDebug Strict PS >$testVariable = "Hello" PS >$tsetVariable += " World" The variable $tsetVariable cannot be retrieved because it has not been set

yet. At line:1 char:14

+ $tsetVariable += " World"

For the sake of your script debugging health and sanity, strict mode should be one of the first additions you make to your PowerShell profile.

When it comes to interactive debugging (as opposed to bug prevention), PowerShell supports several of the most useful debugging features that you might be accustomed to: tracing (through the SetPsDebug –Trace statement), stepping (through the SetPsDebug –Step statement), and environment inspection (through the $host. EnterNestedPrompt() call).

As a demonstration of these techniques, consider Example 133.

Example 133. A complex script that interacts with PowerShell’s debugging features

############################################################################## ## ## InvokeComplexScript.ps1 ## ## Demonstrates the functionality of PowerShell's debugging support. ## ##############################################################################

WriteHost "Calculating lots of complex information"

$runningTotal = 0 $runningTotal += [Math]::Pow(5 * 5 + 10, 2)

WriteDebug "Current value: $runningTotal"

SetPsDebug Trace 1 $dirCount = @(GetChildItem $env:WINDIR).Count

SetPsDebug Trace 2 $runningTotal = 10 $runningTotal /= 2

SetPsDebug Step $runningTotal *= 3 $runningTotal /= 2

$host.EnterNestedPrompt()

SetPsDebug off

As you try to determine why this script isn’t working as you expect, a debugging session might look like Example 134.

Example 134. Debugging a complex script

PS >$debugPreference = "Continue" PS >InvokeComplexScript.ps1 Calculating lots of complex information DEBUG: Current value: 1225

Example 134. Debugging a complex script (continued)

DEBUG: 17+ $dirCount = @(GetChildItem $env:WINDIR).Count DEBUG: 17+ $dirCount = @(GetChildItem $env:WINDIR).Count DEBUG: 19+ SetPsDebug Trace 2 DEBUG: 20+ $runningTotal = 10 DEBUG: ! SET $runningTotal = '1215'. DEBUG: 21+ $runningTotal /= 2 DEBUG: ! SET $runningTotal = '607.5'. DEBUG: 23+ SetPsDebug Step

Continue with this operation? 24+ $runningTotal *= 3

[Y]

Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"):y DEBUG: 24+ $runningTotal *= 3 DEBUG: ! SET $runningTotal = '1822.5'.

Continue with this operation? 25+ $runningTotal /= 2

[Y]

Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"):y DEBUG: 25+ $runningTotal /= 2 DEBUG: ! SET $runningTotal = '911.25'.

Continue with this operation? 27+ $host.EnterNestedPrompt()

[Y]

Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"):y DEBUG: 27+ $host.EnterNestedPrompt() DEBUG: ! CALL method 'System.Void EnterNestedPrompt()' PS >$dirCount

PS >$dirCount + $runningTotal 1207.25 PS >exit

Continue with this operation? 29+ SetPsDebug off

[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"):y DEBUG: 29+ SetPsDebug off

While not built into a graphical user interface, PowerShell’s interactive debugging features are bound to help you diagnose and resolve problems quickly.

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

Event Logs in Windows PowerShell

Event logs form the core of most monitoring and diagnosis on Windows. To support this activity, PowerShell offers the GetEventLog cmdlet to let you query and work with event log data on a system. In addition to PowerShell’s builtin GetEventLog cmdlet, its support for the .NET Framework means that you can access event logs on remote computers, add entries to event logs, and even create and delete event logs.