Tag Archives: Split()

Parsing nslookup: Only Return the Resolved IP

It was late last Friday evening when a vendor and I worked though some maintenance. As a part of the work (which actually lasted well into the weekend), we needed to remove two DNS A records and replace them with CNAME records. I figured that before the change, I would return the current resolution results, so I would be able to ensure the DNS change took place, after the night’s weekend’s work.

You’re probably familiar with the standard output of nslookup.

PS> nslookup subdomain.mydomain.com
Server:  mydomain.com
Address:  10.10.10.1

Name:    subdomain.mydomain.com
Address:  10.20.30.40

These results indicate the name, subdomain.mydomain.com, resolves to an IP address of 10.20.30.40 when using the mydomain.com nameserver, at 10.10.10.1. I wanted to clean up this output, and since I had time to kill while fighting off sleep, I figured — why not? I started by piping the results of nslookup to the Select-String cmdlet looking for the string, ‘Address.’ As you can see in the example below, it returns both the IP of the server where the resolution took place, and the results. After that, there doesn’t seem to be a way to distinguish one line from the other. I needed another way to only return the results.

PS> nslookup subdomain.mydomain.com | Select-String Address

Address: 10.10.10.1
Address: 10.20.30.40

I decided to pipe the command above to the Get-Member cmdlet to see what kind of object the Select-String cmdlet returned. The property that jumped out at me right away was LineNumber — was it really going to be that easy?

   TypeName: Microsoft.PowerShell.Commands.MatchInfo

Name         MemberType Definition
----         ---------- ----------
Equals       Method     bool Equals(System.Object obj)
GetHashCode  Method     int GetHashCode()
GetType      Method     type GetType()
RelativePath Method     string RelativePath(string directory)
ToString     Method     string ToString(), string ToString(string directory)
Context      Property   Microsoft.PowerShell.Commands.MatchInfoContext Context {get;set;}
Filename     Property   string Filename {get;}
IgnoreCase   Property   bool IgnoreCase {get;set;}
Line         Property   string Line {get;set;}
LineNumber   Property   int LineNumber {get;set;}
Matches      Property   System.Text.RegularExpressions.Match[] Matches {get;set;}
Path         Property   string Path {get;set;}
Pattern      Property   string Pattern {get;set;}

Here’s the thing, the output of nslookup is always the same: this on a this line, and that on that line. This is likely why the LineNumber property looked so promising, so quickly. Now that we know we have this property, let’s try and use it. While my first line number guess worked (read, dumb luck), let’s do it wrong first. Consider again our most recent output below.

PS> nslookup subdomain.mydomain.com | Select-String Address

Address: 10.10.10.1
Address: 10.20.30.40

You might think you would want line 3 of our output, but that’s not correct. Even though Select-String has trimmed down the output, the line numbers are all still intact. Look at the very first example at the top of this post. Line 3 is blank — it’s line 5 that we want! Take a look.

PS> nslookup subdomain.mydomain.com | Select-String Address | Where-Object LineNumber -eq 5

Address: 10.20.30.40

If you want to store this as as a string, use the .ToString() method.

PS> (nslookup subdomain.mydomain.com | Select-String Address | Where-Object LineNumber -eq 5).ToString()
Address: 10.20.30.40

And, if you want to remove the word “address,” the colon, and the space, in order to only return the IP address, then you can parse the string by using the .Split() method. The example below splits the string at the space and return the last element — what’s on the right side of the space.

PS> (nslookup subdomain.mydomain.com | Select-String Address | Where-Object LineNumber -eq 5).ToString().Split(' ')[-1]
10.20.30.40

I hope this was helpful for someone. Until next time.

Using Replace() to Fix Split() (and Convert-Path)

I was working on a recent project that required scripting ACLs, and so I had a Windows PowerShell console open in addition to the ISE. This allowed me to quickly check the owner of a directory (or folder). I could press the up arrow to rerun my command, and I could quickly see if the owner had changed according to my script. Let’s say my directory is called ‘TestFolder’ and is located at the root of the C:\ drive.

PS C:\> New-Item -Path TestFolder -ItemType Directory

    Directory: C:\

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----        10/21/2014   9:19 PM            TestFolder

To get the ACL information for the folder, you can use the Get-Acl cmdlet, as in the example below.

PS C:\> Get-Acl -Path .\TestFolder

    Directory: C:\

Path                                    Owner                                   Access
----                                    -----                                   ------
TestFolder                              BUILTIN\Administrators                  BUILTIN\Administrators Allow  FullCo...

Being the PowerShell enthusiast that I am, I modified my command so that only the information I wanted (the path and the owner) was being returned. This is done using the Select-Object cmdlet. Unfortunately, when the command returned those two properties, the Path property was no longer what I was expecting – take a look below. While the example above only returned the name of the folder (TestFolder), I thought I would change this to show the full path (C:\TestFolder) since I was now dealing with a string that included it.

PS C:\> Get-Acl -Path .\TestFolder | Select-Object -Property Path,Owner

Path                                                        Owner
----                                                        -----
Microsoft.PowerShell.Core\FileSystem::C:\TestFolder         BUILTIN\Administrators

The first thing I thought to do was to split the path at the two colons (::) and grab the second element, which I thought would end up being C:\TestFolder. The split method didn’t work so well; here’s what I ended up with.

PS C:\> Get-Acl -Path .\TestFolder | Select-Object -Property @{N='Path';E={($PSItem.Path).Split('::')[-1]}},Owner

Path                                                        Owner
----                                                        -----
\TestFolder                                                 BUILTIN\Administrators

The reason this didn’t work is because the Split() method doesn’t split on each occurrence of two, back-to-back colons like you might expect, it splits on every colon. Since C:\ has a colon, it split there as well. FYI: The use of [-1] returns the last element in an array – good to know, I know. Here’s an example that may help better explain. In this example below, the string is split on every exclamation point (!) and every question mark (?) – not only on the combination of both (!?).

PS C:\> $String = 'Today is the 21st! That is great news, right?'
PS C:\> $String
Today is the 21st! That is great news, right?
PS C:\> $String.Split('!?')
Today is the 21st
 That is great news, right

PS C:\>

What I then decided to do to get this the way I wanted it, was to first replace the two, back-to-back colons with a single character (that was not a part of the string), and then split on that single character. It worked, and here’s what that looks like.

PS C:\> Get-Acl -Path .\TestFolder | Select-Object -Property @{N='Path';E={(($PSItem.Path).Replace('::','@')).Split('@')[-1]}},Owner

Path                                                        Owner
----                                                        -----
C:\TestFolder                                               BUILTIN\Administrators

PS C:\>

It was about this point, that I wanted to see if the -split operator would have handled this the same way and required the additional work that the Replace()/Split() methods did. Of course, after all I did to get this to work how I wanted, I determined I should have started with the -split operator. The -split operator isn’t looking at the characters individually, but instead of, as a whole – two, back-to-back colons is two back-to-back colons.

PS C:\> Get-Acl -Path .\TestFolder | Select-Object -Property @{N='Path';E={(($PSItem.Path) -split '::')[-1]}},Owner

Path                                                        Owner
----                                                        -----
C:\TestFolder                                               BUILTIN\Administrators

A note, if the @{N=…;E={…}} syntax is confusing, or new to you, then spend some time reading this: http://technet.microsoft.com/en-us/library/ff730948.aspx, and then run Get-Help about_Hash_Tables.

This happens just about every time I get to ready to publish a new post. I discovered a better way to handle the problem – much like I did when I considered the -split operator.  It turns out that I could have used a built-in cmdlet to convert the path for me. That’s right, there’s a cmdlet that would have handled everything. Using the Convert-Path cmdlet will convert something like this: Microsoft.PowerShell.Core\FileSystem::C:\TestFolder to this: C:\TestFolder. Here’s the example.

PS C:\> Get-Acl -Path .\TestFolder | Select-Object -Property @{N='Path';E={Convert-Path $PSItem.Path}},Owner

Path                                                        Owner
----                                                        -----
C:\TestFolder                                               BUILTIN\Administrators

PS C:\>

Well, that’s it for this one. As much as I might seem irritated about how I did this three different ways – from the most work to the least – I understand how important this learning process is, and that one day I will be grateful for having gone down this path… (pun intended).