PowerShell Running Select-String on Variable vs on File

18,939

Solution 1

Found an issue with this line:

$input = ipconfig | select-string "IPv4" | select-string -notmatch "192.168.56."

You cannot use $input the way you're trying to use it here. It is one of the default variables in Powershell. Change the instances of $input to $iplist or another variable name.

Updated answer based on your latest edit:

Odd seeing it select IPv4 like that. I don't get the same results here. Try this, it is similar to Matt's method, but skips a couple of the Foreach-Object calls.

$ipAddress = (select-string -inputObject $inputVar -Pattern $regex -AllMatches).tostring().split(" ")[-1]

I should note this may not be the best process if you have multiple network adapters returned. In my case I have three and this only selects the last one.

Solution 2

Chaining Select-String

I think that is the issue you are having. That and you are treating Select-String like Where-Object. Consider the following

ipconfig |  select-string "IPv4" | % { $_.Matches } | % { $_.Value }

The results of this would be at least one string that is IPv4 since that is exactly what you are asking for. Understand that select-string does not return strings but Microsoft.PowerShell.Commands.MatchInfo objects. I can imagine what is going on through your head would be "but my output should the strings?". That is why you see most uses of Select-String use % { $_.Matches } | % { $_.Value } to extract the matched strings.

There is a difference in what PowerShell is willing to show you on console and the actual underlying object. Not sure if you need more explanation. This edit would make your code work in place:

ipconfig |  select-string "IPv4" | out-string | 
    select-string -notmatch "192.168.56." | out-string |
    select-string -Pattern $regex -AllMatches | % { $_.Matches } | % { $_.Value }

Still, that seems a little odd as the first Select-Strings are acting like Where clauses like I already mentioned. You could also do this then:

ipconfig | Where-Object {$_ -match "IPv4" -and $_ -notmatch "192\.168\.56\." } |
    select-string -Pattern $regex -AllMatches | % { $_.Matches } | % { $_.Value }

Other approaches

I don't see any issues with you non-working version but I would like to offer an improvement to you select-string's since you don't exactly need to use more than one with some minor changes.

$ipAddressPrefix = [regex]::Escape("169.254.125.")
$ipaddresses = ipconfig | select-string "IPv4.*?: (?!$ipAddressPrefix).*" | % { $_.Matches } | % {$_.Value.Split(": ")[-1]}

# Pop up an alert window with the IP address
$wshell = New-Object -ComObject Wscript.Shell
$wshell.Popup($ipaddresses,0,"Done",0x1)

Lets take the first 3 octets that you are trying to not match and make an escaped matching string. Then using a negative lookahead we match the lines containing "IPv4" but that don't have the $ipAddressPrefix following the colon.

Then, using all matches returned we split the string on the colon and space to get the IpAddresses matched. Again, note that it is very possible to get more than one address returned in this way.

Preferntial Method

Parsing string to get this information is fine and dandy but you can get it much easier using WMI and not having to working about things like leading and trailing spaces

$ipAddressPrefix = [regex]::Escape("169.254.125.")
$ipaddresses = gwmi Win32_NetworkAdapterConfiguration | Where { $_.IPAddress -and ($_.IPAddress -notmatch "$ipAddressPrefix|:") } |
    Select -Expand IPAddress

$ipaddresses should contain the same results using either code snippet. The Where clause is broken down into these two parts

  1. $_.IPAddress --> this would filter out empty strings and nulls
  2. $_.IPAddress -notmatch "$ipAddressPrefix|:" would ensure that the $ipAddressPrefix matches are ommited. You will also see the : which will omit the IPv6 addresses.
Share:
18,939
will
Author by

will

SOreadytohelp

Updated on June 17, 2022

Comments

  • will
    will almost 2 years

    I'm trying to use a PowerShell script to extract my local network IP address from the output of ipconfig. I have gotten it working using a temporary output file but would like to eliminate that step and just use a variable inside the script. I cannot figure out why my script that isn't using a temporary output file won't work. Any help would be much appreciated.

    Here are the two scripts. First, the working version:

    $input_path = 'C:\Users\Will\Code\Scripts\tmp.txt'
    $regex = '\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b'
    
    ipconfig | select-string "IPv4" | select-string -notmatch "192.168.56." > tmp.txt
    
    $ipAddress = select-string -Path $input_path -Pattern $regex -AllMatches | % { $_.Matches } | % { $_.Value }
    
    # Pop up an alert window with the IP address
    $wshell = New-Object -ComObject Wscript.Shell
    $wshell.Popup($ipAddress,0,"Done",0x1)
    
    # Copy the IP address to the clipboard
    $ipAddress | CLIP
    

    And now the almost identical, but non-working version using only variables:

    $regex = '\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b'
    
    $input = ipconfig | select-string "IPv4" | select-string -notmatch "192.168.56."
    
    #$input > inputVar.txt
    
    $ipAddress = select-string -inputObject $input -Pattern $regex -AllMatches | % { $_.Matches } | % { $_.Value }
    
    # Pop up an alert window with the IP address
    $wshell = New-Object -ComObject Wscript.Shell
    $wshell.Popup($ipAddress,0,"Done",0x1)
    
    # Copy the IP address to the clipboard
    $ipAddress | CLIP
    

    I am using select-string to find the lines containing 'IPv4' piped into another call to select-string to eliminate the line containing the IP address of my virtual machine. If I enable the test output on line 6 of the non-working script and compare it to the 'tmp.txt' file from the working script, I can see that up to this point, both scripts work identically.

    Furthermore, lines 10-15 in both scripts are identical. So I believe that the only line in question is #8.

    I have tried it every way I can think of, but always get the same result ("IPv4" rather than the IP address) The first script works as expected and gives me the IP.

    Both of these options seem like they should work (and are probably the most logical of all that I've tried), but neither gives me the IP:

    $ipAddress = select-string -inputObject $input -Pattern $regex -AllMatches | % { $_.Matches } | % { $_.Value }
    

    and

    $ipAddress = $input | select-string -Pattern $regex -AllMatches | % { $_.Matches } | % { $_.Value }
    

    I've even tried eliminating all the extra steps and just making one long pipe like this:

    $ipAddress = ipconfig | select-string "IPv4" | select-string -notmatch "192.168.56." | select-string -Pattern $regex -AllMatches | % { $_.Matches } | % { $_.Value }
    

    and just copying to the clipboard like this:

    ipconfig | select-string "IPv4" | select-string -notmatch "192.168.56." | select-string -Pattern $regex -AllMatches | % { $_.Matches } | % { $_.Value } | CLIP
    

    But nothing seems to work.

    Where have I gone wrong with this?

    Thanks!


    Updates 03/13/2015 6:30pm

    I'm running Windows 7 64bit

    Here's the output of $PSVersionTable:

    Name Value
    ---- -----
    CLRVersion 2.0.50727.5485
    BuildVersion 6.1.7601.17514
    PSVersion 2.0
    WSManStackVersion 2.0
    PSCompatibleVersions {1.0, 2.0}
    SerializationVersion 1.1.0.1
    PSRemotingProtocolVersion 2.1

    And yes, my end goal is just to get the IP. Really, I just wanted to find a quick way to do that. I guess I didn't technically need the IP by itself for what I'm using it for, but then once I got into it, I got pretty caught up in trying to figure the rest of this out just out of curiosity.

    Thanks for all the help!

    I'm just getting back into coding after a ~10 year break and I'm super rusty.

    I'm not sure why my non working version isn't working for me if it's working for you. When I run it, I get "IPv4" in the pop up and on the clipboard, rather than the IP (which I get in the working version that uses the temp file.

    I'm going to integrate these regex changes to my current script and play around with your gwmi solution, Matt. An initial run isn't returning anything for me, but I'll toy around with it and let you know.

    Let me know if you have any more ideas on why the version with the variable isn't working correctly on my end in light of this updated info. It's totally baffling to me.

    Thanks again!


    Updates 03/13/2015 10:27pm

    Here's the output of $PSVersionTable after the update:

    Name Value
    ---- -----
    PSVersion 4.0
    WSManStackVersion 3.0
    SerializationVersion 1.1.0.1
    CLRVersion 4.0.30319.18444
    BuildVersion 6.3.9600.16406
    PSCompatibleVersions {1.0, 2.0, 3.0, 4.0}
    PSRemotingProtocolVersion 2.2


    Here is what happens if I run the Piped Commands piece by piece in PowerShell. As you can see, it seems to break on line 14 at: $ipAddress = $ipAddress | % { $_.Matches }

    Anyhow, thought this might help:

    PS C:\Users\Will\Code> $regex = '\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b'
    PS C:\Users\Will\Code> $inputVar = ipconfig | select-string "IPv4" | select-string -notmatch "192\.168\.56\."
    PS C:\Users\Will\Code> $inputVar
    
       IPv4 Address. . . . . . . . . . . : 192.168.0.105
    
    
    PS C:\Users\Will\Code> $ipAddress = select-string -inputObject $inputVar -Pattern $regex -AllMatches
    PS C:\Users\Will\Code> $ipAddress
    
       IPv4 Address. . . . . . . . . . . : 192.168.0.105
    
    
    PS C:\Users\Will\Code> $ipAddress = $ipAddress | % { $_.Matches }
    PS C:\Users\Will\Code> $ipAddress
    
    
    Groups   : {IPv4}
    Success  : True
    Captures : {IPv4}
    Index    : 3
    Length   : 4
    Value    : IPv4
    
    
    
    PS C:\Users\Will\Code> $ipAddress = $ipAddress | % { $_.Value }
    PS C:\Users\Will\Code> $ipAddress
    IPv4
    PS C:\Users\Will\Code>
    
  • will
    will about 9 years
    Just tried this, but unfortunately it didn't fix my issue. It's still very good to know that though, and I'll avoid using $input as a variable from here on out! Thanks!
  • will
    will about 9 years
    Thanks for all this, Matt! I'm going to play around with this and see if I can get it working on my end. An initial run didn't produce any results, but I expect it's user error at this point. I appreciate all the help. I updated my original post with the info that Daniel Lindegaard asked for, in case that helps at all.
  • Booga Roo
    Booga Roo about 9 years
    Interesting. Looks like other people are reporting that the non-working code works for them. There's gotta be something else in the mix like different Powershell versions or environment settings.
  • will
    will about 9 years
    I totally agree. I'm just not sure what it is! I'm glad you pointed out the out of date version of PS I had though. It's got to be good to get up to date, whether it fixed this particular problem or not.
  • will
    will about 9 years
    This totally works, and it'll likely be my solution at this point. But I'd still like to figure out what's going on with that other code.