ForEach and ForEach-Object Differences

This topic contains 7 replies, has 5 voices, and was last updated by Profile photo of Cantoris Cantoris 2 years, 3 months ago.

  • Author
    Posts
  • #18216
    Profile photo of Tim Pringle
    Tim Pringle
    Participant

    After far too long of not doing it, I've started using the full version of PowerShell commands (i.e. no aliases) and their parameters. Along with this, I'm dropping use of the '| ' pipeline character from my scripts. Here's my problem.

    I used to use ForEach in something like the format below as an example:

    Import-Module ActiveDirectory
    $users = Get-ADUser -Filter *
    ForEach ($user in $users) {
    [string] $name = $user.name
    [string] $message = "Hello $name"
    Write-Host $message
    }

    If i want to migrate this to ForEach-Object without using pipeline symbol, i would think that i could do the following :

    Import-Module ActiveDirectory
    $users = Get-ADUser -Filter *
    ForEach-Object -InputObject $users -process {
    [string] $name = $_.name
    [string] $message = "Hello $name"
    Write-Host $message
    }

    But when in the second one, the value of '$_', it is the array itself

    So question is, is it possible for me to use the foreach-object to replicate the first example, without the use of the pipeline symbol?

    thanks,

    Tim

  • #18217
    Profile photo of Richard Siddaway
    Richard Siddaway
    Moderator

    Why do you want to drop the use of the pipeline – you lose one of PowerShell's greatest strengths

  • #18218
    Profile photo of Tim Pringle
    Tim Pringle
    Participant

    Hi Richard,

    Sorry, I didn't word that good. What i mean is that if i can, i find

    ForEach ($user in $users) {
    ..
    ..
    ..
    }

    looks better than than

    $users | ForEach-Object {
    ..
    ..
    ..
    }

    So if i don't need to use the the ' | ' character i won't.

    Best practices are to use the full command name and properties, but I'm not certain how i'd do this with ForEach-Object other than use the ' | ' symbol, since -inputobject doesnt seem to work the way i thought it would.

  • #18222
    Profile photo of Simon Wåhlin
    Simon Wåhlin
    Participant

    Hi Tim,

    I think there is a misunderstanding here.

    The keyword foreach is not the same thing as the cmdlet Foreach-Object.

    Example using the keyword:

    $Users = Get-ADUser -Filter *
    foreach($User in $Users)
    {
       'Some code here'
    }
    

    Here foreach is a keyword, for more information about this, check Get-Help about_Foreach.
    This method requires that all objects are stored in the $Users variable and if there are many it could be heavy on memory usage.

    Here is an example using the Cmdlet Foreach-Object (which can also be referenced to by its two aliases foreach and %):

    Get-ADUser -Filter * | Foreach-Object { 'Some code here' }
    

    In this example, Get-ADUser will immediately send each user retrieved from AD to Foreach-Object that can start to process the objects as they are retrieved.
    This is a leaner approach since only the objects currently being processed are stored in memory and we don't have to wait for AD to return all objects before we start processing them.

    For more information on the Cmdlet Foreach-Object, see Get-Help Foreach-Object.

  • #18223
    Profile photo of Tim Pringle
    Tim Pringle
    Participant

    Thanks Simon,

    Ah, okay, thats starting to make sense. So if i understand right ForEach-Object would only ever normally be used when it is prefixed with an object that is piped to it, or if you wanted to pass an entire object in one via -inputobject?

    I think the thing that's really thrown me is that ForEach is listed as an alias for ForEach-Object in PowerShell.

    PS C:\Windows\system32> Get-Alias -Name foreach

    CommandType Name Source
    ———– —- ——
    Alias foreach -> ForEach-Object

    So i thought it would be possible for me also to use the same format of command I'd used in ForEach. I was searching for the property of the ForEach-Object command to use that would allow me to do that.

  • #18225
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    ForEach-Object only works with pipeline input. Even if you could use it as the first or only command in a pipeline, it would be far slower than using the foreach keyword.

    That said, I wouldn't recommend going to either extreme (using only [b]$users | ForEach-Object[/b] or only [b]foreach ($user in $users)[/b]). These have different performance characteristics, with the keyword typically being faster but at the expense of requiring more memory. If you force yourself into never using the pipeline, you're eventually going to run into OutOfMemoryExceptions when handling large sets of data.

  • #18227
    Profile photo of Tim Pringle
    Tim Pringle
    Participant

    thanks Dave,

    I'll work on the basis that if i know the size of the dataset and it's reasonable, i'll use foreach, otherwise foreach-object.

  • #18248
    Profile photo of Cantoris
    Cantoris
    Participant

    One more thing, with
    foreach ($object in $collection) {}
    but not with
    $objects | ForEach-Object {}
    you can use the continue keyword to jump to the next item in the collection like you can with the various looping constructs.

You must be logged in to reply to this topic.