PowerShell: Why the Count Property or += Don’t Work Sometimes

One thing that I occasionally forget (and it can take me a while to remember the fix) is some apparently strange behaviour when processing an array.  Sometimes array-based methods don’t work when there’s only one item (or no items!) to be processed.

It’s all down to PowerShell’s automatic type conversion and is pretty easy to fix.

The bugs for this come up when I write scripts to process a list of objects, normally from a CSV or the pipeline.  I write the code to process the array after it’s been created and it’s all great!  Then later on I get some bizarre behaviour when the list of items to process is empty or only has one entry.

So let’s say we had a CSV file like;


If I then import that into a variable;

$Records=Import-CSV c:\names.csv

I can then see the following;

$Records.Count -eq 0
$Records.Count -gt 0

So PowerShell returns the count of items in $Records is neither equal to 0 nor greater than 0.  Any logic in my script that uses that comparison would see some odd results, to say the least.

I get a straight error if I tried to add a new record to the variable;

PS C:\Users\Neil> $Records+=New-Object -TypeName PSObject -Property @{Name="Newuser";Email="Newuser@herringsfishbait.com
Method invocation failed because [System.Management.Automation.PSObject] does not contain a method named 'op_Addition'.
At line:1 char:1
+ $Records+=New-Object -TypeName PSObject -Property @{Name="Newuser";Em ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (op_Addition:String) [], RuntimeException
    + FullyQualifiedErrorId : MethodNotFound

Both of these problems come from the fact that $Records isn’t, in fact, an array.  In the case where I’ve imported a CSV with only one record, PowerShell helpfully sets the type of $Records to an Object, rather than an array;

PS C:\Users\Neil> $Records.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    PSCustomObject                           System.Object

PS C:\Users\Neil> @("1","2").GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

So in order to stop this happening, you need to force PowerShell to set $Records to an array (cast it to an array) even if there’s only one element being imported into it.  Then everything works as you expect.

PS C:\Users\Neil> [PSObject[]]$Records=Import-csv D:\temp\test.csv
PS C:\Users\Neil> $Records.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     PSObject[]                               System.Array

PS C:\Users\Neil> $Records.Count

You could also do;

PS C:\Users\Neil> $Records2=@(Import-csv D:\temp\test.csv)
PS C:\Users\Neil> $Records2.Count


Categories: Powershell, Technology

Tags: , , , , , , , ,

3 replies

  1. THANK YOU! I was using an AD object searcher to store results then adding additional results to that same collection.
    $colResults = @()
    $colResults = $objSearcher.FindAll()
    $objSearcher.SearchRoot = “another OU”
    $colResults += $objSearcher.FindAll()

    This worked on every datacenter except for one and was driving me nuts! Your article led me to:
    $colResults = @($objSearcher.FindAll())
    $colResults += $objSearcher.FindAll()

    Thank you much!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: