PowerShell in Depth, Second Edition (2015)
Part 2. PowerShell management
Chapter 13. Regular expressions
This chapter covers
· Regular expression syntax
· The –match operator
· Regular expressions in Select-String
· Regular expressions in the switch statement
· The REGEX object
Regular expressions are a powerful and complex language you can use to describe data patterns. Most pattern-based data, including email addresses, phone numbers, IP addresses, and more, can be represented as a “regex,” as they’re also known.
We need to set some limits for this chapter, because regular expressions are complex enough to deserve their own book. PowerShell uses industry-standard regex syntax and supports the full .NET regular expression library, but we’ll only be covering the basics. Most of our focus will be on how PowerShell uses a regex once you’ve written one. If you’d like to explore regular expressions further, write more complex expressions, or look for a regex that meets a particular need, we recommend one of these two books as a starting place: Mastering Regular Expressions by Jeffrey E.F. Friedl (a favorite), and Regular Expression Pocket Reference by Tony Stubblebine, both from O’Reilly. You can also visit http://RegExLib.com, a free community repository of regular expressions for specific tasks.
13.1. Basic regular expression syntax
A simple regular expression is a literal string. For example, the regular expression “Don” will match any instance of “Don,” although in PowerShell such matching is case insensitive by default. Where regular expressions show their power is when you start using wildcards, character classes, and placeholders instead of literal strings. Table 13.1 shows some of the basic syntax, which is also covered in PowerShell’s about_regular_expressions help file. We’ve included some additional comments and clarified a few points.
Table 13.1. Basic regular expression syntax
Regex symbol |
Purpose |
Example |
. |
Any single character. |
“d.n” would match both “don” and “dan” but not “dean.” |
[abc] |
Matches at least one of the characters in the set enclosed by the square brackets. The comparison is not case sensitive. |
“d[oae]n” would match “don” and “dan” but wouldn’t match “dean,” because the string contains an extra letter. It also wouldn’t match “din” because “i” isn’t in the set. |
[a-z] |
A shorter way of specifying an entire range of characters or numbers. The comparison is not case sensitive. |
“d[a-e]n” would match “dan” and “den” but not “don.” |
[^o] |
Matches any character except those in brackets; you can also precede a range with ^ to match any character except one in that range. The comparison is not case sensitive. |
“d[^e]n” would match both “don” and “dan” but not “den.” |
^ |
Indicates that the pattern must start at the beginning of the string. |
“^op” would match “operate,” because the pattern—“op”—occurs at the start of the string. “Loop” wouldn’t be a match, because “op” doesn’t happen at the start of the string. |
$ |
Indicates the end of the string in the pattern. |
“op$” would match “hop,” because “op” occurs at the end of the string. “Hope” wouldn’t be a match, because “op” is followed by “e” rather than the end of the string. |
* |
Matches zero or more instances of the preceding character or character sets. |
“g*” would match “bag” and “baggy,” because both contain zero or more “g” characters. Be careful here because “baddy” will also be true, because there are zero instances of the character g. Perhaps a better example is matching 2.19 and .76 to “\d*\.\d*,” which will match on any number with a decimal point, regardless of whether the decimal point contains a number to its left. |
+ |
Matches one or more repeating instances of the character or character set. |
“abc+” would match on all instances of “abc” in a string like “abcDabcEabcF”. Often this will give you the same result as using *. |
? |
Matches exactly zero or one instance of the preceding character or character set. |
“g?” would match “bag” and “baggy,” because both contain one “g.” The fact that “baggy” contains an extra “g” doesn’t stop the match. Be careful with this one as well because even if you have zero matches you’ll still get a match. Your best approach may be to incorporate a quantifier, which we’ll cover later in this chapter. |
\ |
Escapes a character that normally is a regex symbol, treating it as a literal value. |
“192\.168” would match “192.168” because the “\.” matches a literal period. Conversely, “192.168” would match both “192.168” and “192x168,” because the period is matching any single character. |
Regular expressions work around the concept of matching: Either the pattern described by the regex matches a given string or it doesn’t. If PowerShell has a match, it’ll give you $True. We typically use regex matches in IF statements and Where-Object script blocks. You’ve already seen matching in a simplistic form when you used wildcards. In the filesystem, for example, the wildcard pattern “*.tmp” would match all files ending in “.tmp,” including “this.tmp” and “that.tmp.” Those filesystem wildcards are, in essence, a super-simplified kind of regex.
Typing out all the characters you might want in a match can be tedious, even when using ranges. For that reason, regex syntax includes character classes, several of which are shown in table 13.2. (Although the table isn’t a comprehensive list, they’re the ones you’re most likely to use in PowerShell.) These classes stand in for any character contained within that class.
Table 13.2. Regular expression classes
Regex symbol |
Purpose |
Example |
\w |
Any word character, including any letter, number, or underscore, but not most punctuation and no whitespace. |
“\w*” would match “Hello.” It would also match “Hello There,” because it would match the “Hello” portion before stopping at the space between the two words. |
\s |
Any whitespace, including tabs, carriage returns, and spaces. |
“\s” would match the space between the two words in “Hello There.” Reducing the comparison to “Hello” wouldn’t match because there isn’t any whitespace, even though “Hello” does match. |
\d |
Any digit—that is, numbers 0 through 9. |
“\d” would match “10.” It would also match the digits in “12 Monkeys” but wouldn’t match “Zebras” at all. |
\W |
Match anything except a word character. |
It would match on the spaces in “jeff” but not on “jeff.” |
\S |
Match anything except a space character. This includes tabs. |
Note that "Hello " -match "\S" "Hello " -match "\S" "`tHello" –match "\S" all return TRUE because a nonspace character could be found. The matching character will be the H. We’ll explain the –match operator in a moment. |
\D |
Match anything except a digit character. |
The string “P0w3rShell” will match on everything except the 0 and 3. |
The three examples for the \S regex symbol are where PowerShell is case sensitive, without being explicitly told to behave that way. The uppercase versions of those classes represent the “opposite,” that is, anything that isn’t contained in the class.
You can see from these class examples that it’s not always sufficient to know that some portion of a string has matched your regex; you may also need to know that no portion of the string failed to match, or you may need to know which part of the string matched your expression. Those are somewhat trickier tasks, and we’ll analyze them in some of the examples later in this chapter.
One way to be more precise with your expressions is to use quantifiers. The major ones are listed in table 13.3.
Table 13.3. Basic regular expression quantifiers
Regex symbol |
Purpose |
Example |
* |
Match zero or more instances. |
“\w*” matches “hello.” |
+ |
Match repeating patterns of the preceding characters. |
“xy\d+” would match “xy7xy6.” |
? |
Match zero or one of the preceding characters. |
“\w?” would match the “x” in “7x24.” |
{n} |
Match exactly that many times. |
“a{2}” would match “aa” but not “a.” |
{n,} |
Match at least that many times, but also match more. |
“a{2,} would match “aa” and “aaa” but not “a.” |
{n,m} |
Match at least that many times (n) and no more than this many times (m). |
“a{2,3}” would match “aa” and “aaa” but not “a.” But it will also match “aaaa,” because the match contains at least two characters but not more than three. The match will be on the first three characters. This is where anchors come in handy, for example, “^a{2,3}$.” |
Parentheses can also be used to group subexpressions. For example, “(\d{1,3}\.){3}” would match “192.168.12.” because the group contains one to three digits followed by a period repeated three times. There’d be no match on “192.168.” But be careful because “192.168.12.1.1” will also match unless you use an anchor like “^(\d{1,3}\.){3}$.” where you force the match to occur at the beginning of the string.
13.2. The –match operator
PowerShell’s –match operator, its case-sensitive –cmatch version, and the logical opposites –notmatch and –cnotmatch (case-insensitive versions -imatch and –inotmatch also exist but aren’t used much because PowerShell is case insensitive by default) all use regular expressions. The left side of the operator is a string to test, whereas the right side is the regex to test against. If you match a single string, the result is $True if there’s a match and $False if there’s no match. In addition, PowerShell autopopulates a collection called $matches with the matches it’s detected. Using that collection, you can see exactly what PowerShell matched against, for example:
PS C:\> 'don' -match '\w'
True
PS C:\> $matches
Name Value
---- -----
d
In the previous example, you can see that “don” does match the regex “\w” and, more specifically, that it was the first letter, “d,” that made the match.
You can also match multiple strings, in which case the result of the operator is a collection of the strings that matched:
PS C:\> 'one','two','three' -match '\w{3}'
one
two
three
When used in this fashion, $matches isn’t populated. Looking at that example carefully, you might be surprised to see “three” in the output. There’s the tricky bit of a regex. It was the “thr” that matched the regex, which in turn caused the entire string to output. If you want to have three characters and then the end of the string, use the $ anchor:
PS C:\> 'one','two','three' -match '\w{3}$'
one
two
three
Whoops—that time it matched the “ree” in “three.” Let’s also add the ^ anchor:
PS C:\> 'one','two','three' -match '^\w{3}$'
one
two
There you go. This illustrates the difficulty of working with regular expressions—you have to be careful to make sure they’re rejecting the right data, as well as matching the right data. For example, see if you think this is a legitimate regex for a Universal Naming Convention (UNC) path such as \\Server\Share\Folder\File.txt:
PS C:\> '\\server\share\folder\file.txt' -match '\\\\\w+([\w\.]\\+)'
True
Seems fine, but let’s try a deliberately malformed UNC:
PS C:\> 'x\\server\share\folder\file.txt' -match '\\\\\w+([\w\.]\\+)'
True
That shouldn’t have matched, so you clearly need to tweak it:
PS C:\> 'x\\server\share\folder\file.txt' -match '^\\\\\w+([\w\.]\\+)'
False
And now you can make sure the original, valid UNC still works:
PS C:\> '\\server\share\folder\file.txt' -match '^\\\\\w+([\w\.]\\+)'
True
Again, these can be tricky. Can you think of any other ways to fool this regex or to improve it? Let’s break down what it’s doing in table 13.4.
Table 13.4. Deconstructing a regex
Regex pattern |
Purpose |
^ |
Anchors the start of the pattern to the start of a string, ensuring no characters can come before the leading \\. |
\\\\ |
Matches the leading \\ in the UNC. Keep in mind that \ is a special character in regex syntax, so you have to double them up to “escape” them. |
\w+ |
Matches one or more word characters—this is what picks up the server name in the UNC. |
( |
Starts a repeating pattern. |
[\w\.] |
Accepts a word character or a period. |
\\ |
Followed by a backslash. |
+ |
One or more times. |
) |
Ending the repetition. |
Breaking it down like that, we suspect other problems may exist with it. You won’t always end a UNC in a backslash, for example. Maybe the following would be better?
PS C:\> '\\server\share\folder\file.txt' -match '^\\\\\w+([\w\.\\]+)+'
True
We’ll leave it to you to decipher and decide if you can improve on it. For what it’s worth, the online regex library we use documents at least five regular expressions designed to detect various kinds of file paths, including UNCs: http://regexlib.com/Search.aspx?k=unc&c=2&m=-1&ps=20. That reinforces how tricky a regex can be to write.
Tip
Even if you decide to write your own regex, it’s always worth checking regexlib.com to determine if there’s a more efficient way.
13.3. The Select-String cmdlet
The Select-String cmdlet is designed to look through a bunch of data, finding data that matches a pattern you provide. You can pipe in strings or, better yet, point the command to a directory full of files. Here’s one example, which searches for all files containing the word “shell”:
PS C:\Windows\System32\WindowsPowerShell\v1.0\en-US> Select-String -Pattern
"shell" -SimpleMatch -Path *.txt -List
about_Aliases.help.txt:6: PowerShell.
about_Arithmetic_Operators.help.txt:5: Describes the operators that
perform arithmetic in Windows PowerShell.
about_Arrays.help.txt:9: of the same type. Windows PowerShell supports
data elements, such as
about_Assignment_Operators.help.txt:13: Windows PowerShell supports
the following assignment operators.
<display truncated for brevity>
This example used the command’s –SimpleMatch parameter, which tells it to treat the pattern not like a regex but like a text string. As you can see, it lists all files with a match, the matching line number, and the matching line itself. Now try this with a regex:
PS C:\Windows\System32\WindowsPowerShell\v1.0\en-US> Select-String -Pattern
"\sGet-\w{4,8}\s" -Path *.txt -List
about_Aliases.help.txt:89: get-help about_profile
about_Arithmetic_Operators.help.txt:397:C:\PS> get-date + $day
about_Arrays.help.txt:50: Microsoft .NET Framework. For example, the
objects that Get-Process
about_Assignment_Operators.help.txt:113: $a = get-service | sort
name
<display truncated for brevity>
You asked for matches on a pattern that specified a space, “Get-,” and four to eight characters followed by another space. These are only partial results; run the command on your own—in the same directory used here—to see the full list.
A variety of other parameters are available on the command to customize its behavior further, and it’s a great way to (for example) scan through text log files looking for a particular pattern of characters.
13.4. The Switch statement
You can also use regular expressions in the Switch statement. You’ll use the –regex parameter to indicate this. Consider the example in the following listing.
Listing 13.1. A regex Switch example
'abcd', 'Abcd', 'abc1', '123a', '!>@#' |
foreach {
switch -regex -case ($_){
"[a-z]{4}" {"[a-z]{4} matched $_"}
"\d" {"\d matched $_"}
"\d{3}" {"\d{3} matched $_"}
"\W" {"\W matched $_"}
default {"Didn't match $_"}
}
}
Which of the input strings didn’t match?
The tests can be decoded as follows:
· “[a-z]{4}” means match four characters, each of which is a letter in the range a to z.
· “\d” means match a digit.
· “\d{3}” means match three digits.
· “\W” means match a non-word character.
When we tested this code we received the following output:
[a-z]{4} matched abcd
Didn't match Abcd
\d matched abc1
\d matched 123a
\d{3} matched 123a
\W matched !>@#
Why didn’t “Abcd” match? At a PowerShell prompt it works:
PS C:\> "Abcd" -match "[a-z]{4}"
True
So why did it not match in our code snippet? The catch is that we also used the -case parameter in the Switch statement. It makes the matches case sensitive, which is the equivalent of doing the following:
PS C:\> "Abcd" -cmatch "[a-z]{4}"
False
The next listing shows one more example that might be a bit more practical.
Listing 13.2. Another regular expression Switch example
$Computername="LON-DC01"
Switch -regex ($Computername) {
"^SYR" {
#run Syracuse location code
}
"^LAS" {
#run Las Vegas location code
}
"^LON" {
#run London location code
}
"DC" {
#run Domain controller specific code
}
"FP" {
#run file/print specific code
}
"WEB" {
#run IIS specific code
}
Default {Write-Warning "No code found $computername"}
}
Listing 13.2 contains a code excerpt from something you might want to do. The Switch statement will evaluate the $computername variable using a series of regular expression patterns. Wherever there’s a match, the corresponding script block will execute. In the example, this would mean PowerShell would run any code specific to the London location and the domain controller role, assuming our servers follow a standard naming convention.
13.5. The REGEX object
Most of the time simple matching or not matching is sufficient. But regular expressions have some special capabilities such as finding all the matches in a string or replacing matched text. To accomplish these tasks you need to create a REGEX object. The easiest approach is to use the [regex] type accelerator with a regular expression pattern. Start with something simple:
PS C:\> [regex]$rx="Don"
Piping the object to Get-Member reveals some interesting possibilities:
PS C:\> $rx | get-member
TypeName: System.Text.RegularExpressions.Regex
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetGroupNames Method string[] GetGroupNames()
GetGroupNumbers Method int[] GetGroupNumbers()
GetHashCode Method int GetHashCode()
GetType Method type GetType()
GroupNameFromNumber Method string GroupNameFromNumber(int i)
GroupNumberFromName Method int GroupNumberFromName(string name)
IsMatch Method bool IsMatch(string input), bool IsMatch(...
Match Method System.Text.RegularExpressions.Match Matc...
Matches Method System.Text.RegularExpressions.MatchColle...
Replace Method string Replace(string input, string repla...
Split Method string[] Split(string input), string[] Sp...
ToString Method string ToString()
Options Property System.Text.RegularExpressions.RegexOptio...
RightToLeft Property System.Boolean RightToLeft {get;}
Don’t use the REGEX object with –Match. Instead, invoke the Match() method. With this object, case matters:
PS C:\> $rx.Match("don")
Groups : {}
Success : False
Captures : {}
Index : 0
Length : 0
Value :
PS C:\> $rx.Match("Don")
Groups : {Don}
Success : True
Captures : {Don}
Index : 0
Length : 3
Value : Don
PS C:\> $rx.Match("Richard")
Groups : {}
Success : False
Captures : {}
Index : 0
Length : 0
Value :
The object writes a new object to the pipeline. The Match() method returns an object only on the first match. Look at the following:
PS C:\> $rx.Match("Let me introduce Don Jones. Don is a PowerShell MVP")
Groups : {Don}
Success : True
Captures : {Don}
Index : 17
Length : 3
Value : Don
If you want to identify all of the matches of “Don,” use the Matches() method:
PS C:\> $rx.Matches("Let me introduce Don Jones. Don is a PowerShell MVP")
Groups : {Don}
Success : True
Captures : {Don}
Index : 17
Length : 3
Value : Don
Groups : {Don}
Success : True
Captures : {Don}
Index : 28
Length : 3
Value : Don
This code will write a Match object for each match.
13.5.1. Replacing with REGEX
Where this gets even more interesting is when it comes time to replace matched text:
PS C:\> $rx.Replace("Let me introduce Don Jones. Don is a PowerShell
MVP","Donald")
Let me introduce Donald Jones. Donald is a PowerShell MVP
The REGEX object replaces all the matching instances of the pattern “Don” in the string with “Donald.”
The Replace() method from the String object as well as the –Replace operator are using regular expressions under the hood. You can use regular expression patterns:
PS C:\> "172.16.10.12" -replace "\d{3}","XXX"
XXX.16.10.12
13.5.2. Splitting with REGEX
Another technique we want to show with the REGEX object is splitting data on the regular expression match. Let’s say you want to parse the contents of the Windows Update log. The log contains lines like the following:
$s="2012-03-14 18:57:35:321 1196 13f8 PT Server URL =
http://172.16.10.1/SimpleAuthWebService/SimpleAuth.asmx"
Suppose you want to split this line on the time stamp value (18:57:35:321) so you construct a REGEX object:
[regex]$r="\d{2}:\d{2}:\d{2}:\d{3}"
In order to split, it has to match, so you’ll test:
PS C:\> $r.match($s)
Groups : {18:57:35:321}
Success : True
Captures : {18:57:35:321}
Index : 11
Length : 12
Value : 18:57:35:321
Excellent. Now you can split the string on the pattern match:
PS C:\> $r.split($s)
2012-03-14
1196 13f8 PT Server URL =
http://172.16.10.1/SimpleAuthWebService/SimpleAuth.Asmx
The second line in the previous example is wrapping, but it worked. Notice that the matching data is gone. That happens because you used that as the split “character” so all you’re left with is everything before it, the date, and everything after it. Depending on your pattern, you may have extra spaces to take care of, but that’s simple to fix:
PS C:\> $a=$r.split($s) | foreach {$_.trim()}
PS C:\> $a[1]
1196 13f8 PT Server URL =
http://172.16.10.1/SimpleAuthWebService/SimpleAuth.aSmx
The Trim() method deletes any leading or trailing spaces from the string. Extending this to the entire file, you can split each line and save the second part to a variable:
PS C:\> $data=get-content C:\windows\WindowsUpdate.log |
foreach {$r.split($_)[1].Trim()}
You can then do whatever you want with $data. We’ll come back to the Windows Update log in a moment.
13.6. Subexpressions and named captures
The last item we want to demonstrate with regular expressions and PowerShell is the ability to use subexpressions and named captures. Consider this simple example:
PS C:\> "aaa 123 bbb 456" -match "\d{3}"
True
PS C:\> $matches
Name Value
---- -----
0 123
The –Match operator found the first three-digit match. A subexpression, at its simplest, is the pattern wrapped in parentheses:
PS C:\> "aaa 123 bbb 456" -match "(\d{3})"
True
PS C:\> $matches
Name Value
---- -----
1 123
0 123
One subtle but important change is that the Match object has two values now. One of them is for the subexpression:
PS C:\> "aaa 123 bbb 456" -match "(\d{3}) (\w{3})"
True
PS C:\> $matches
Name Value
---- -----
2 bbb
1 123
0 123 bbb
Now we have subexpression matches for numbers and text. Let’s take the next step and use a REGEX object so we can capture everything:
PS C:\> [regex]$rx = "(\d{3}) (\w{3})"
PS C:\> $m =$rx.Matches("aaa 123 bbb 456")
PS C:\> $m
Groups : {123 bbb, 123, bbb}
Success : True
Captures : {123 bbb}
Index : 4
Length : 7
Value : 123 bbb
PS C:\> $m.groups
Groups : {123 bbb, 123, bbb}
Success : True
Captures : {123 bbb}
Index : 4
Length : 7
Value : 123 bbb
Success : True
Captures : {123}
Index : 4
Length : 3
Value : 123
Success : True
Captures : {bbb}
Index : 8
Length : 3
Value : bbb
If we wanted to get the match of just the letters or numbers, that gets a little tricky. Fortunately, the solution is to use a named capture. All you need to do is to define a name for your capture like this:
PS C:\> "aaa 123 bbb 456" -match "(?<word>\w{3}) (?<num>\d{3})"
True
Now for the cool part. The Match object now shows these names:
PS C:\> $matches
Name Value
---- -----
word aaa
num 123
0 aaa 123
We can get the name we want like this:
PS C:\> $matches.num
123
PS C:\> $matches.word
aaa
When a REGEX object is used, you have to access the matches using the defined names:
PS C:\> [regex]$rx="(?<word>\w{3}) (?<num>\d{3})"
PS C:\> $m = $rx.Matches("aaa 123 bbb 456")
The tricky part is that $m contains several types of objects:
PS C:\> $m | foreach {$_.groups["num"].value}
123
456
PS C:\> $m | foreach {$_.groups["word"].value}
aaa
bbb
Here’s a more practical example. Suppose you get a string that looks like a UNC and you want to get the server and share name:
PS C:\> $p = "\\server01\data"
You could split the string on the last \ into an array and know that the server name would be element 0 and the share name element 1. Or you could use a named capture:
PS C:\> $p -match "^\\\\(?<server>\w+)\\(?<share>\w+$)"
True
PS C:\> $matches.server
server01
PS C:\> $matches.share
Data
Let’s end this section by returning to the Windows Update log. Earlier we showed you how to split each line. Because the log file has a known and consistent layout, you can use a regular expression named capture and “objectify” the log file.
Note
We realize there are other ways to import the log into PowerShell, but this is a log everyone has and serves as a good learning tool.
We’ll walk through the process using a sample line from the log:
$t = "2014-01-07 10:23:48:211 880 a9c IdleTmr Incremented PDC
RefCount for Network to 2"
The layout uses the format DATE TIME PID TID COMPONENT TEXT, so you need to define a set of regular expression patterns to capture the various elements:
$t -match "(?<Date>\d{4}-\d{2}-\d{2})\s+(?<Time>(\d{2}:)+\d{3})\s+
(?<PID>\d+)\s+(?<TID>\w+)\s+(?<Component>\w+)\s+(?<Message>[.*)"
The match result shows success:
PS C:\> $matches
Name Value
---- -----
Message Incremented PDC RefCount for Network to 2
Time 10:23:48:211
Date 2014-01-07
Component IdleTmr
PID 880
TID a9c
1 48:
0 2014-01-07 10:23:48:211 880 a9c...
Now let’s use the REGEX object:
[regex]$rx = "(?<Date>\d{4}-\d{2}-\d{2})\s+(?<Time>(\d{2}:)+\d{3})\s+
(?<PID>\d+)\s+(?<TID>\w+)\s+(?<Component>\w+)\s+(?<Message>.*)"
An advantage with this approach is that you can identify the names:
PS C:\> $rx.GetGroupNames()
0
1
Date
Time
PID
TID
Component
Message
The first two aren’t our names, so they can be filtered out:
$names = $rx.GetGroupNames() | where {$_ -match "\w{2}"}
For our proof of concept, we’ll get the first five lines of the log file:
$t = get-content C:\windows\WindowsUpdate.log | select -first 5
The variable $t is a collection of five lines that need to be “decoded” individually:
$data = $t | foreach {
$rx.Matches($_) | foreach {
$match = $_
$names | foreach -begin {$hash=[ordered]@{}} -process {
$hash.Add($_,$match.groups["$_"].value)
} -end { [pscustomobject]$hash}
}
}
In the code we get all the matches using the REGEX object. Each match is then processed using a nested ForEach-Object loop to get the corresponding named match. Each match is added to an ordered hash table that’s using the Name as the key and Match as the value. After processing all names, PowerShell writes a custom object to the pipeline using the hash table. Everything is saved to $data.
PS C:\> $data
Date : 2014-01-20
Time : 00:45:08:210
PID : 1112
TID : 24b4
Component : Report
Message : WARNING: CSerializationHelper:: InitSerialize failed :
0x80070002
Date : 2014-01-20
Time : 00:45:08:211
PID : 1112
TID : 24b4
Component : Report
Message : CWERReporter::Init succeeded
Date : 2014-01-20
Time : 00:45:08:211
PID : 1112
TID : 24b4
Component : Agent
Message : *********** Agent: Initializing Windows Update Agent
***********
Date : 2014-01-20
Time : 00:45:08:211
PID : 1112
TID : 24b4
Component : Agent
Message : * Found 2 persisted download calls to restore
Date : 2014-01-20
Time : 00:45:08:212
PID : 1112
TID : 24b4
Component : DnldMgr
Message : Download manager restoring 1 downloads
Because everything is now an object, the log file can be viewed any way you want:
PS C:\> $data | where component -eq 'agent' |
select Date,Time,Message | format-table –AutoSize -Wrap
Date Time Message
---- ---- -------
2014-01-20 00:45:08:211 *********** Agent: Initializing Windows
Update Agent ***********
2014-01-20 00:45:08:211 * Found 2 persisted download calls to restore
You could use these techniques for just about any log file.
13.7. Summary
Remember, the REGEX object is based on a regular expression pattern that can be as complicated as necessary. Be careful to watch how matches are made and be sure to test for failures as well when developing a regular expression.
Tip
Don’t try to reinvent the wheel. Visit sites like Regexlib.com to look for a pattern that someone has already developed. In most cases you can use the pattern as is in your PowerShell scripting.
Regular expressions are, no question, a complex language all their own. We’ve briefly touched on the main ways in which PowerShell uses them, as well as on the basics of regex syntax, including the REGEX object. We know there’s more to regex than we could cover in this chapter, and if you’d like to explore them further, please check out the resources we mentioned in the beginning of the chapter.