Search code examples
powershellwindows-server

List objects vs. Arrays in PowerShell


So I was working on a script and found an unusual behavior:

$Drives = Get-WmiObject -Class Win32_logicalDisk -Filter 'DriveType=3' | Select -ExpandProperty DeviceID #this will exclude CD drives and shared drives, etc, returning only local logical drives.
$MisconfiguredConfigFiles = [List[object]]::new()
$AllConfigFiles = [List[object]]::new()

foreach ($drive in $Drives) {
    $ConfigFiles = dir "$drive\*.config" -Recurse
    $AllConfigFiles.Add($ConfigFiles.FullName)
}

If I do $AllConfigFiles.Count with that code, it gives me the number of drives instead of the number of files. But if I do this:

$Drives = Get-WmiObject -Class Win32_logicalDisk -Filter 'DriveType=3' | Select -ExpandProperty DeviceID #this will exclude CD drives and shared drives, etc, returning only local logical drives.
$MisconfiguredConfigFiles = [List[object]]::new()
$AllConfigFiles = @()

foreach ($drive in $Drives) {
    $ConfigFiles = dir "$drive\*.config" -Recurse
    $AllConfigFiles += $ConfigFiles.FullName
}

Then, $AllConfigFiles.Count will return the accurate number of files I was expecting. How can I continue to use the List object (and its correlated "Add" method) instead of an array using the "+=" operator?


Solution

  • The "direct" answer is Mathias': you are adding a collection, so you should use .AddRange().
    += unpacks the collection automatically but that obviously is a problem if you want to add a collection as a single item :)

    That said, the best way to deal with your needs is direct assignment.

    # DO NOT USE WMI CMDLETS. It has been Obsoleted snce Powershell 3.  
    # And completely removed from 6+ .    
    # Use CIM ones instead.  
    # Force-casting removes a few hassle in calling the drive later on.  
    # Specifying the property on Get-CimInstance is mostly superfluous optimization
    # in this case: it makes it so the cmdlet only returns that property of the
    # device.  
    # I added it mostly as a Teaching Moment(TM) about Left Side Filtering.    
    [System.IO.DriveInfo[]]$DriveList = Get-CimInstance -Class Win32_logicalDisk -Filter 'DriveType=3' -Property DeviceID |     
        Select-Object -ExpandProperty DeviceID 
    
    # Force-casted direct assignment FTW.   
    # If you do NOT need to add\remove items AFTER the collection has been created,
    # remove the casting and you'll have a nice Array perfectly fine to use.
    [System.Collections.Generic.List[object]]$AllConfigFiles = foreach ($Drive in $DriveList) {
        # Evaluate adding -Force, -Depth and -ErrorAction SilentlyContinue .  
        Get-ChildItem -LiteralPath $Drive -File -Filter '*.config' -Recurse 
    }
    
    $AllConfigFiles.Count