Using Select-Object Correctly?

Hello,

I’m having a hard time using the Select-Object to output only two of four objects available in the output.

For example, the output below shows Index, Name, Description and Size. I would like to output only Index and Name.

Perhaps I’m wrong about these being objects…? If they are, am I just using Select-Object incorrectly?

My code looks like this:

$wrs_local="C:\Mounts\WindowsRepairSource"
dism /Get-WimInfo /WimFile:"$wrs_local\RepairSource.wim"

The code results in the following output:

Deployment Image Servicing and Management tool
Version: 10.0.19041.844

Details for image : C:\Mounts\WindowsRepairSource\RepairSource.wim

Index : 1
Name : Windows 10 Pro for Workstations
Description : Windows 10 Pro for Workstations
Size : 15,074,686,984 bytes

Index : 2
Name : Windows 11 Pro
Description : Windows 11 Pro
Size : 16,198,033,078 bytes

The operation completed successfully.

Output repeats for each Index; up to #6 in my case.

I thought I could use Select-Object to limit the output to only Index and Name.

dism /Get-WimInfo /WimFile:"$wrs_local\RepairSource.wim" | Select-Object Index,Name

However, when I do, I only get this:

Index Name
----- ----

… followed by a bunch of blank lines with 10 empty spaces each, including one that I think is supposed to contain the following: The operation completed successfully.

I was hoping to get something like this:

Deployment Image Servicing and Management tool
Version: 10.0.19041.844

Details for image : C:\Mounts\WindowsRepairSource\RepairSource.wim

Index : 1
Name : Windows 10 Pro for Workstations

Index : 2
Name : Windows 11 Pro

The operation completed successfully.

Or just this:

Index : 1
Name : Windows 10 Pro for Workstations

Index : 2
Name : Windows 11 Pro

I’m using Microsoft’s Select-Object for reference.

Any insight would be greatly appreciated.

Since DISM.exe is an external tool - not a PowerShell cmdlet you cannot expect it to output its output as PowerShell objects. It is text. :wink:
If you want to limit the output to some particular text you will have to parse it … you may try something like this:

dism /Get-WimInfo /WimFile:"$wrs_local\RepairSource.wim" | 
    Foreach-Object {
        if ($_ -match 'Index|Name') {
            $_
        }
    }

But you should keep in mind - the output is still text/strings. :point_up_2:t4:

2 Likes

DISM supports a /Format parameter with table/list, but unfortunately this particular command is not compatible with the table option. I’d just parse it like this.

$wrs_local="C:\Mounts\WindowsRepairSource"

$output = switch -Regex (dism /Get-WimInfo /WimFile:"$wrs_local\RepairSource.wim"){
    '^Index : (.+$)' {
        $ht = [ordered]@{
            Index = $Matches.1
        }
    }

    '^(Name|Description) : (.+$)' {
        $ht.Add($Matches.1,$Matches.2)
    }

    'Size : (.+) bytes$' {
        $ht.Add('Size',"{0:N2} GB" -f ([decimal]($matches.1 -replace '\.|,') / 1GB))
        [PSCustomObject]$ht
    }
}

# output only for your eyes in the console
$output | Format-List

# use the $output variable however you need
$output | Select-Object Index, Name

$output | Export-Csv -Path \path\to\file.csv -NoTypeInformation

Edit

I ended up making a function for it. Feel free to use it if you’d like.

Function Get-WimInformation {
    <#
    .SYNOPSIS
        Extract image information from Wim files
    .DESCRIPTION
        Extract image information from Wim files and outputs easy to use objects
    .EXAMPLE
        PS C:\> Get-WimInformation -Path E:\Mount\RepairSource.wim
    .EXAMPLE
        PS C:\> 'E:\Mount\RepairSource.wim' | Get-WimInformation
    .EXAMPLE
        PS C:\> Get-ChildItem E:\Mount -Filter *.wim | Get-WimInformation
    .INPUTS
        string
    .NOTES
        https://forums.powershell.org/t/using-select-object-correctly/18715
        https://github.com/krzydoug/Tools/blob/master/Get-WimInformation.ps1
    #>

    [cmdletbinding()]
    Param(
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName,Position=0,HelpMessage="Enter the full path to the wim file")]
        [Alias("FullName","WimFile")]
        [string]$Path
    )

    begin{
        $dism = Get-ChildItem C:\Windows\System32 -Filter dism.exe | Select-Object -ExpandProperty FullName
    }

    process{
        switch -Regex (&$dism /Get-WimInfo /WimFile:$Path){
            
            '^Details for image : (.+$)' {
                $obj = [PSCustomObject]@{
                    WimFile     = $Matches.1
                    Index       = ''
                    Name        = ''
                    Description = ''
                    Size        = ''
                }
            }

            '^(Index|Name|Description) : (.+$)' {
                $obj.($Matches.1) = $Matches.2
            }

            'Size : (.+) bytes$' {
                $obj.Size = "{0:N2} GB" -f ([decimal]($matches.1 -replace '\.|,') / 1GB)
                $obj
                'Index','Name','Description','Size' | ForEach-Object {$obj.$_ = ''}
            }
        }
    }
}
3 Likes

Hello @Olaf,

I ran your script in PowerShell, and it returned the following:

Index : 1
Name : Windows 10 Pro for Workstations
Index : 2
Name : Windows 11 Pro
Index : 3
Name : Windows 11 Pro for Workstations
Index : 4
Name : Windows Server 2022 Datacenter
Index : 5
Name : Windows Server 2022 Datacenter (Desktop Experience)
Index : 6
Name : Windows 10 Enterprise LTSC 2021

Which is exactly what I was looking to achieve.
I was playing around with where to place the Out-File and I was able to get it to work by adding it here, along with my variable:

$wrs_local="C:\Mounts\WindowsRepairSource"
dism /Get-WimInfo /WimFile:"$wrs_local\RepairSource.wim" | 
    Foreach-Object {
        if ($_ -match 'Index|Name') {
            $_ | Out-File -FilePath "$wrs_local\RepairSource-WinInfo.txt" -Append # Output WIM file contents
        } 
    }

This will definitely work for my needs. Now I just need to look more into parsing text in more detail. I use DISM a lot, and always felt it was limited for outputting information that I would like to use later on when I learn how to better use it with an IF for example.

Thanks for doing this…

Hello @krzydoug,

I was playing around with your Function and trying out the three examples that you provided with it. One thing I noticed was that it’s throwing errors for the part that’s trying to reformat the numbers for the Size.

Although I’m not looking to get more than the Index and Name from the results at this time, I can see a need for this when gathering this information for a variety of wim files. The Size will come in handy when tracking images as they grow after being patched/updated over time.

Output example:

Cannot convert value "17,861,833,565" to type "System.Int32". Error: "Input string was not in a correct format."
At line:49 char:17
+                 $obj.Size = "{0:N2} GB" -f ($Matches.1 / 1GB)
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvalidCastFromStringToInteger
 
WimFile     : C:\Mounts\WindowsRepairSource\RepairSource.wim
Index       : 6
Name        : Windows 10 Enterprise LTSC 2021
Description : Windows 10 Enterprise LTSC 2021
Size        : 

I was playing around with trying to remove or comment out the Description and Size elements, but I didn’t have any luck. I just made it worse. I tried changing this to this:

Description = '' to #Description = ''
Size = '' to #Size = ''
(Index|Name|Description) to (Index|Name)
'Index','Name','Description','Size' to 'Index','Name'

I would also like to comment out the WimFile path from the output for now. This is necessary information when running this against multiple WIM files if I can loop this.

I’m very impressed with the Function. It doesn’t look easy, and I’m grateful for the time you took to make it. I hope that one day I’ll be able to create something like this. :+1:

Seems like the output has thousands separators. So you change the line number 49 to this:

$obj.Size = "{0:N2} GB" -f (($Matches.1 -replace '\.|,') / 1GB)

Regardless of that - when you use the function and you don’t want to have all the properties you don’t have to change the function. You simply select what you need like this:

Get-WimInformation -Path "<Path to a valid WIM file>" |
    Select-Object -Property Index, Description, Size
1 Like

I left the commas in mine. My first thought is maybe that number is larger than an int. I’ll have to check but either way I’ll update the function. Thanks for the feedback guys!

It only took a few minutes. You are quite welcome. I’ve updated the function to work for your case and I also edited my original post. If you have several wim files you can simply pipe Get-Childitem output to the function

Get-ChildItem -Path Some\Folder -Filter *.wim | Get-WimInformation | Select-Object Index, Name

The error message stays the same:

Cannot convert value "19.074.156" to type "System.Decimal". Error: "Input string was not in a correct format."
At line:49 char:17
+ ...                $obj.Size = "{0:N2} GB" -f ([decimal]$Matches.1 / 1GB)
+                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvalidCastFromStringToDecimal

… or the v7.2.1 version of the error:

InvalidArgument:
Line |
  49 |  …                $obj.Size = "{0:N2} GB" -f ([decimal]$Matches.1 / 1GB)
     |                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | Cannot convert value "19.074.156" to type "System.Decimal". Error: "Input string was not in a correct format."

Does it really work for you with the thousands separators?

1 Like

Yes it does, I’ll update again with your suggestion and show my results later.

1 Like

I’ve added your suggestion and here is the same working on my system

Oh and if the number is less than an int it didn’t require anything on my system, hence my suggested code that didn’t work for you guys. “But it works on my system!” LOL

image

1 Like

Hello @Olaf,

I applied your change recommendation to @krzydoug’s Function and no errors:
Change:

$obj.Size = "{0:N2} GB" -f (($Matches.1 -replace '\.|,') / 1GB)

Output:

WimFile     : C:\Mounts\WindowsRepairSource\RepairSource.wim
Index       : 1
Name        : Windows 10 Pro for Workstations
Description : Windows 10 Pro for Workstations
Size        : 14.04 GB

Plus I was able to filter the Function output as you recommended:

$wrs_wim="C:\Mounts\WindowsRepairSource\RepairSource.wim"
Get-WimInformation -Path "$wrs_wim" | Select-Object -Property Index, Name

Results:

Index Name                                               
----- ----                                               
1     Windows 10 Pro for Workstations                    
2     Windows 11 Pro                                     
3     Windows 11 Pro for Workstations                    
4     Windows Server 2022 Datacenter                     
5     Windows Server 2022 Datacenter (Desktop Experience)
6     Windows 10 Enterprise LTSC 2021

I piped my Out-File to get those results in a text file:

$wrs_wim="C:\Mounts\WindowsRepairSource\RepairSource.wim"
Get-WimInformation -Path "$wrs_wim" | Select-Object -Property Index, Name `
| Out-File -FilePath "$wrs_local\RepairSource-WinInfo.txt" -Append # Output WIM file contents

I’m loving this new output where it’s side by side. It’s PERFECT! :+1:

Thank you both very much!

It get’s even worse … even though my system is set up to German I still have to use the English thousands separators in PowerShell


:thinking:

The default output of up to a maximum of 4 properties is always formatted as table. :wink:

1 Like