Search code examples
powershelltext-parsingpscustomobject

How can i convert the output of prnmngr into custom object?


output of cscript prnmngr.vbs -l

Server name abcd 
Printer name \\abcd.com\mailroom
Share name mailroom
Driver name Canon iR-ADV 4225/4235 UFR II
Port name mailroom.com
Comment
Location
Print processor winprint
Data type RAW
Parameters
Attributes 536
Priority 1
Default priority 0
Average pages per minute 0
Printer status Idle
Extended printer status Unknown
Detected error state Unknown
Extended detected error state Unknown

Server name cdef 
Printer name \\cdfet.com\mailroom3
Share name mailroom3
Driver name Canon iR-ADV 4225/4235 UFR II
Port name mailroomxxx.com
Comment
Location
Print processor winprint
Data type RAW
Parameters
Attributes 536
Priority 1
Default priority 0
Average pages per minute 0
Printer status Idle
Extended printer status Unknown
Detected error state Unknown
Extended detected error state Unknown

something like (note the modified output property names):

$CustomPrinterobjects = New-Object –TypeName PSObject
$CustomPrinterobjects | Add-Member –MemberType NoteProperty –Name ComputerName –Value "$a" 
$CustomPrinterobjects | Add-Member –MemberType NoteProperty –Name Name –Value "$b" 
$CustomPrinterobjects | Add-Member –MemberType NoteProperty –Name ShareName –Value "$c" 
$CustomPrinterobjects | Add-Member –MemberType NoteProperty –Name DriverName –Value "$d"
$CustomPrinterobjects | Add-Member –MemberType NoteProperty –Name PortName –Value "$e"

where $a, $b, $c,$d, $e represent property values looped over the output of cscript prnmngr.vbs -l


Solution

  • Kory Gill helpfully suggests using the W8+ / W2K12+ Get-Printer cmdlet instead.

    Similarly, kuujinbo suggests Get-WmiObject -Class Win32_Printer for earlier OS versions.

    In the spirit of PowerShell, both commands returns objects whose properties you can access directly - no need for text parsing.


    In case you still have a need to parse the output from cscript prnmngr.vbs -l (if it provides extra information that the cited commands do not), use the following approach - note how much effort is needed to parse the textual output into structured objects:

    Given that all information is space-separated and the property name part of each line is composed of varying numbers of tokens, the only predictable way to parse the text is to:

    • maintain a collection of well-known property names
    • consider whatever comes after the property name on the line the value.

    A PSv3+ solution:

    # Map the input property names of interest to output property names,
    # using a hashtable.
    $propNameMap = @{ 
      'Server name ' = 'ComputerName'
      'Printer name ' = 'Name'
      'Share name ' = 'ShareName'
      'Driver name ' = 'DriverName'
      'Port name ' = 'PortName'
    }
    
    # Split the output of `cscript prnmngr.vbs -l` into paragraphs and 
    # parse each paragaph into a custom object with only the properties of interest.
    $customPrinterObjs = (cscript prnmngr.vbs -l) -join "`n" -split "`n`n" | ForEach-Object {
      $ohtFields = [ordered] @{}
      foreach ($line in $_ -split "`n") {
        foreach ($propNamePair in $propNameMap.GetEnumerator()) {
          if ($line -like ($propNamePair.Key + '*')) {
            $ohtFields[$propNamePair.Value] = $line.Substring($propNamePair.Key.length)
          }
        }
      }
      [pscustomobject] $ohtFields
    }
    
    # Output the resulting custom objects.
    $customPrinterObjs
    

    With your sample input, the above yields a 2-element [pscustomobject] array:

    ComputerName : abcd 
    Name         : \\abcd.com\mailroom
    ShareName    : mailroom
    DriverName   : Canon iR-ADV 4225/4235 UFR II
    PortName     : mailroom.com
    
    ComputerName : cdef 
    Name         : \\cdfet.com\mailroom3
    ShareName    : mailroom3
    DriverName   : Canon iR-ADV 4225/4235 UFR II
    PortName     : mailroomxxx.com
    
    • (cscript prnmngr.vbs -l) -join "`n" collects the output lines from cscript prnmngr.vbs -l in an array and then joins them to form a single multiline string.

    • -split "`n`n" splits the resulting multiline string into paragraphs, each representing a single printer's properties.

    • The ForEach-Object script block then processes each printer's properties paragraph:

      • foreach($line in $_ -split "`n") splits the multiline paragraph back into an array of lines and loops over them.
      • $ohtFields = [ordered] @{} initializes an empty ordered hashtable (where entries are reflected in definition order on output) to serve as the basis for creating a custom object.
      • The inner foreach loop then checks each line for containing a property of interest, and, if so, adds an entry to the output hashtable with the output property name and the property value, which is the part that follows the well-known property name on the line.
      • Finally, the ordered hashtable is output as a custom object by casting it to [pscustomobject].