-1

I have been having issues finding an answer to this question. SO I figured I would share what I have built with the masses. What I was trying to do was run the ARP -a command and capture the results to use in a TXT file for something else later. After much search, I was able to compile this.

I have included notes in the code to help those are less experienced to know what each section does.

But this code does these components

  1. Set your output path (if needed)
  2. Looks up current machines IP Address
  3. Breaks down the IP scheme to host only the first 3 octets. *There is a line of code in there that is # if you are using a machine with multiple NICs or IP Addresses to allow you to filter only the single one IF needed.
  4. Captures the ARP Table. *I have to 2 versions of the ARP capture. The first is IF you are going to filter by the devices IP scheme. This will help remove DNS and loop back entries. The second is if you want EVERYTHING.
  5. Outputs the results of IP Addresses to a txt file.

In my case, I am only using it to filter out the IP addresses. You may need it for other purposes. Good news is that it is easy to change the pieces to allow you to filter more towards your needs.

    #Captures ARP Table then Displays ALL IPs that match the local computers IP Address

#Set the file you wish for the data to Output to
$OutputPath = "C:\Temp\Test.txt"

#Captures the devices IP Scheme and break it down, remove the Last Octet from the string
#This section is only used IF you are trying to filter the ARP by the local network the device is on.
[string[]]$ComputerName = $env:computername
$OrgSettings = Get-WmiObject Win32_NetworkAdapterConfiguration -ComputerName $ComputerName -EA Stop | ? { $_.IPEnabled }
$ip = $OrgSettings.IPAddress[0]
#IF (!($ip[1] -eq $null)) {$ip = $ip[0]}
$ip = (([ipaddress] $ip).GetAddressBytes()[0..2] -join ".") + "."
$ip = $ip.TrimEnd(".")

#Searches the ARP table for IPs that match the scheme and parses out the data into an Array (It removed the Devices IP from the list.)
Remove-Variable macarray
$macarray = @()
#(arp -a) -match $ip | Foreach{ #Use if needed for filtering results
(arp -a) | Foreach{    #Use this IF no filtering needed
      $obj = New-Object PSObject -Property @{
        IP  = ($_ -split "\s+")[1]
        MAC = ($_ -split "\s+")[2]
      }
     IF (!($obj.MAC -eq "---" -or $obj.MAC -eq "Address" -or $obj.MAC -eq $null -or $obj.MAC -eq "ff-ff-ff-ff-ff-ff")) {$macarray += $obj}
  }

#Outputting the IP Addresses captured.
$macarray | Select -ExpandProperty "IP" | Out-file -FilePath $OutputPath -Force
JasonMArcher
  • 14,195
  • 22
  • 56
  • 52
Ross G
  • 1
  • 1
  • 1

1 Answers1

0

Here is something that I use when parsing text based tables. This code works when the headers are left aligned with the data below them. The trick is that arp will show data for multiple interfaces. If you need to filter based on that then this logic will generate a custom object array that you can filter or output to file, now with Export-CSV, as needed. If you need more about what is going on inside the loops, it comes from another answer of mine.

This approach is based on the position of the header fields. Nothing is hardcoded and it is all custom build based on those indexes and field names. Using those $headerIndexes we carve up every line and place the results, if present, into its respective column. There is logic to ensure that we don't try and grab any part of the string that might not exist and treat the last field special.

$arpResults = arp.exe -a | Out-String
$arpResults | Select-string -Pattern '(?smi)((?<=Interface:).*?(?=Interface:|\Z))' -AllMatches | 
        Select-Object -ExpandProperty Matches | ForEach-Object{
    $interfaceData = $_.Groups[0].Value.Trim() -split "`r`n"

    # Header of the data table is the second array element.
    $headerString = $interfaceData[1]
    $headerElements = $headerString -split "\s{2,}" | Where-Object{$_}
    $headerIndexes = $headerElements | ForEach-Object{$headerString.IndexOf($_)}

    # Skip the first two lines as they are for the interface address and header for each table
    $interfaceData | Select-Object -Skip 2 | ForEach-Object{
        $props = @{Interface = $interfaceData[0].Trim()}
        $line = $_
        For($indexStep = 0; $indexStep -le $headerIndexes.Count - 1; $indexStep++){
            $value = $null            # Assume a null value 
            $valueLength = $headerIndexes[$indexStep + 1] - $headerIndexes[$indexStep]
            $valueStart = $headerIndexes[$indexStep]
            If(($valueLength -gt 0) -and (($valueStart + $valueLength) -lt $line.Length)){
                $value = ($line.Substring($valueStart,$valueLength)).Trim()
            } ElseIf ($valueStart -lt $line.Length){
                $value = ($line.Substring($valueStart)).Trim()
            }
            $props.($headerElements[$indexStep]) = $value    
        }
        [pscustomobject]$props
    } 

} | Select-Object Interface, "Internet Address","Physical Address",Type

Sample Partial Output

Interface               Internet Address Physical Address  Type   
---------               ---------------- ----------------  ----   
10.10.13.153 --- 0xb    224.0.0.252      01-00-5e-00-00-fc static 
10.10.13.153 --- 0xb    228.5.6.7        01-00-5e-05-06-07 static 
10.10.13.153 --- 0xb    239.255.255.250  01-00-5e-7f-ff-fa static 
10.10.13.153 --- 0xb    255.255.255.255  ff-ff-ff-ff-ff-ff static 
169.254.215.111 --- 0xc 169.254.255.255  ff-ff-ff-ff-ff-ff static 
169.254.215.111 --- 0xc 224.0.0.22       01-00-5e-00-00-16 static 

As BenH points out in comments ..

Windows8/Server2012 or greater has the Get-NetNeighbor cmdlet.

Community
  • 1
  • 1
Matt
  • 45,022
  • 8
  • 78
  • 119