netsh result to a PowerShell object

7k views Asked by At

I am trying to work with NETSH from PowerShell. I want see a result from this command such as an object, but netsh returns a string:

netsh wlan show hostednetwork | Get-Member
   TypeName: System.String
...

My script must work on system with rather localization, and I can't use -match for parsing a string to an object directly.

How I can solve my trouble?

3

There are 3 answers

0
vonPryz On

You got a few alternatives, none of which are nice.

1) Read the netsh output into a string[] and use a custom record parser to create your own object. That is, look at the output on different locales and find out if, say, Hosted newtork settings is always the first header followed by bunch of - characters. If that's the case, assume that next element in array is Mode and so on. This is very error prone, but usually MS command line tools only translate messages, not their order.

2) Look for .Net API for the same information. There is System.Net.NetworkInformation which contains a bunch of connection things. It's a start, though I am not sure if it has info you need.

3) Failing the previous options, use P/Invoke to call native Win32 API. It's a lot of work, so look for pre-existing wrapper libraries before rolling your own.

0
Michael Sorens On

I recently wrote a cmdlet to parse arbitrary, multi-line text using regular expressions, called ConvertFrom-Text. (Not a great name, if you ask me, but it conforms to the PowerShell naming rules; suggestions are welcome!) So assuming you have that cmdlet, here is one possible solution to your question. (Caveat emptor! The regular expression given was derived from a very small sample of netsh output, so may need some tuning.)

$regex = [regex] '(?ms)(?:^\s*$\s*)?^(?<section>.*?)\s*-+\s*(?<data>.*?)\s*^\s*$'
$result = netsh wlan show hostednetwork | Out-String |
    ConvertFrom-Text -pattern $regex -multiline
$result | % {
    $dataObj = [PsCustomObject]@{}
    $_.Data -split "`r`n" | % {
        $element = $_ -split '\s*:\s*'
        Add-Member -InputObject $dataObj -MemberType NoteProperty -Name $element[0].Trim() -Value $element[1].Trim()
    }
    $_.Data = $dataObj # Replace data text with data object
}
$result

On my test system, netsh wlan show hostednetwork returns this:

Hosted network settings
-----------------------
    Mode                   : Allowed
    Settings               : <Not configured>

Hosted network status
---------------------
    Status                 : Not available

And the output of the $result variable in the code above yields this:

section                   data
-------                   ----
Hosted network settings   @{Mode=Allowed; Settings=<Not configured>}
Hosted network status     @{Status=Not available}

So $result is an array of objects with section and data properties, and the latter is an object with properties defined by the output of the netsh command.


Of course, the above does not get you very far without the ConvertFrom-Text cmdlet. So here is the implementation. (I have copious documentation and examples for it, which will be publicly available once I eventually add it to my open-source PowerShell library.)

filter ConvertFrom-Text
{
    [CmdletBinding()]
    Param (
    [Parameter(Mandatory=$true,Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
    [string[]]$InputObject,

    [Parameter(Mandatory=$true,Position=1)]
    [regex]$Pattern,

    [switch]$RequireAll,
    [switch]$Multiline
    )

    if ($Multiline) {
        $dataString = $InputObject -join "`n"
        IterateByMatch $dataString $Pattern
    }
    else {
        IterateByLine $InputObject $Pattern
    }
}

function IterateByLine([string[]]$data, [regex]$regex)
{
    $data | ForEach-Object {
        if ($PSItem -match $regex)
        {
            New-Object PSObject -Property (GetRegexNamedGroups $matches)
        }    
        elseif ($RequireAll) {
            throw "invalid line: $_"
        }
    }
}  

function IterateByMatch([string[]]$data, [regex]$regex)
{
  $regex.matches($data) | Foreach-Object {
    $match = $_
    $obj = new-object object
    $regex.GetGroupNames() |
    Where-Object {$_ -notmatch '^\d+$'} |
    Foreach-Object {
      Add-Member -InputObject $obj NoteProperty `
          $_ $match.groups[$regex.GroupNumberFromName($_)].value
    }
    $obj
  }
}

function Get-RegexNamedGroups($hash)
{
    $newHash = @{};
    $hash.keys | ? { $_ -notmatch '^\d+$' } | % { $newHash[$_] = $hash[$_] }
    $newHash 
} 
0
D. Smith... On
$netshResult = Invoke-Command -Computername localhost {netsh int tcp show global}
$result = @{}
$netshObject = New-Object psobject -Property @{
    ReceiveSideScalingState = $Null
    ChimneyOffloadState = $Null
    NetDMAState = $Null
} 
$netshResult = $netshResult | Select-String : #break into chunks if colon  only
$i = 0
while($i -lt $netshResult.Length){
    $line = $netshResult[$i]
    $line = $line -split(":")
    $line[0] = $line[0].trim()
    $line[1] = $line[1].trim()
    $result.$($line[0]) = $($line[1])
    $i++
    }
$netshObject.ReceiveSideScalingState = $result.'Receive-Side Scaling State'
$netshObject.ChimneyOffloadState = $result.'Chimney Offload State'
$netshObject.NetDMAState = $result.'NetDMA State'