Not a question / just sharing: Get a count of objects from the pipeline

I had a discovery today! I instantly went to this thread to share - https://powershell.org/forums/topic/count-pipeline-objects-within-beginprocessend-blocks/ - but it is closed.

What happened was, while working on my script, I decided to put a Write-Debug at the very top of my begin block, then jumped in to see what all is going on in there. While playing around in here I realized, any time a variable is piped into my script, I can do this to get the count:

begin {

  # Let's try to get a count from the pipeline.
  $PipelineObjectCount = (Get-Variable ($PSCmdlet.MyInvocation.Line -replace '.*\$|\s.*','') -ValueOnly).Count
  if ($PipelineObjectCount -is [int] -and $PipelineObjectCount -gt 0) {

    $MailboxCounter = 0
    $MailboxPercentCompletePossible = $true
  }

The reason is this, my $PSCmdlet.MyInvocation.Line is :

$Mailboxes | .\Get-MailboxTrustees.ps1

So all I need to do is use -replace to wipe off the dollar sign, and then the space after my piped variable, and anything that follows. What’s left is my variable’s name which I can then use Get-Variable -ValueOnly to figure out the count!!!

Later on, I had done this instead, which is one of my habits (Clear-Host):

cls; $Mailboxes | .\Get-MailboxTrustees.ps1

That is why my -replace has the .* before the dollar sign:

-replace '.*\$|\s.*',''

This trick is only good for when the pipeline came from a variable. But that’s pretty OK by me, and I’m thinking similar techniques could be done to accommodate other scenarios.

You mean like Measure-Object CMDLET? you can pipe any objects to it and it will tell you a count value!

[quote quote=143390]You mean like Measure-Object CMDLET? you can pipe any objects to it and it will tell you a count value!

[/quote]
Ooh, that would work too, thanks for the tip.

 

I’d like to share my updated approach where I’m using Measure-Object (thanks again!), and trying to cater to more scenarios than my initial attempt.

Here it is (goes in my begin {} block):

$MyInvocationLineSplit = $PSCmdlet.MyInvocation.Line -split '\|'
$MyInvocationLineSplit = $MyInvocationLineSplit -replace 'cls',''
$MyInvocationLineSplit = $MyInvocationLineSplit -replace 'Clear-Host',''

$InvocationNameMatch = $MyInvocationLineSplit |
    Where-Object {$_ -like "*$($PSCmdlet.MyInvocation.InvocationName)*"}

$InvocationNameIndex = $MyInvocationLineSplit.IndexOf($InvocationNameMatch)

$MeasureableCommand = ($MyInvocationLineSplit | Select-Object -Index (0..($InvocationNameIndex - 1))) -join '|'

$PipelineObjectCount = (Invoke-Expression -Command $MeasureableCommand | Measure-Object).Count

While it’s not fully error-proof, it does work a lot of the time. Performance-wise, in my case I’m writing my scripts for Exchange, which happens to already protect me in this situation from large Get- commands being piped directly into my script (it’ll throw concurrent/busy pipeline errors for me). So my personal worst case in this regard is not too bad.

The above code works for me in my Get-MailboxUsage.ps1 script, tested with quite a few iterations of how one might call the script. This command will work, for example:

"cls;$Users | Select-Object -Index 8,20,22 | .\Desktop\Get-MailboxUsage.ps1 -Verbose | out-null"

I successfully pull a count of 3 out of this, and display progress with -PercentComplete.