Nested ForEach ?

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

  • Author
    Posts
  • #12807
    Profile photo of Aurock
    Aurock
    Participant

    I put together a script that searches for a user's profile on a list of computers and removes the profile if found. It works, but now I'm trying to clean it up and break it into a function or two, add help comments, logging, etc. Initially, I had a pair of nested ForEach loops:


    ForEach ($c in $ComputerName) {. . .
    ForEach ($u in $UserName) { . . . }
    }

    I am given understand that for the purposes of writing output to a log file, it would be better to pipe the variables to ForEach-Object. Is that correct? If so, how do I handle the objects inside the nested foreach loops? Referencing both the username and computername as $_ would be confusing at best, and in my case it wouldn't work as I need to reference the computer as well as the user from inside the 2nd Foreach.

  • #12809
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    It's generally a good practice to save the value of $_ to some other variable at the start of your ForEach-Object loops. That way if some other piece of code reassigns $_ later, you don't lose the value you needed.

    That said, if you already have your $ComputerName and $UserName arrays in memory, there's really not much advantage to using ForEach-Object over foreach. The main difference is that you can pipe the results of ForEach-Object to another command, but there are ways to get around that even if you're using foreach. Here's one trick I use on occasion when I want to use foreach but still have it pipe results to something else:

    # presumably, these are both arrays of strings that you want to enumerate over.
    
    $computerName = @()
    $userName = @()
    
    & {
        foreach ($computer in $computerName)
        {
            foreach ($user in $userName)
            {
                [pscustomobject] @{
                    ComputerName = $computer
                    UserName = $user
                }
            }
        }
    } |
    Export-Csv -Path .\test.csv -NoClobber
    
    

    By placing the foreach loops into an anonymous script block (curly braces) and invoking that script block with the call operator (&), the objects produced by the inner loop will be piped to Export-Csv one at a time, just as ForEach-Object allows you to do. Instead of an anonymous script block, you can also just put them into a function, like this:

    function Some-FunctionName
    {
        [CmdletBinding()]
        param (
            [string[]]
            $ComputerName,
    
            [string[]]
            $UserName
        )
    
        foreach ($computer in $ComputerName)
        {
            foreach ($user in $UserName)
            {
                [pscustomobject] @{
                    ComputerName = $computer
                    UserName = $user
                }
            }
        }
    }
    
    # presumably, these are both arrays of strings that you want to enumerate over.
    
    $computerName = @()
    $userName = @()
    
    Some-FunctionName -ComputerName $computerName -UserName $userName |
    Export-Csv -Path .\test.csv -NoClobber
    
    

    The end result is the same.

  • #12812
    Profile photo of Aurock
    Aurock
    Participant

    Ahh, so if the nested ForEach are inside a function, I would pipe the output to a log file when the function is called, rather than as a part of the function... that will work great, Thanks!

You must be logged in to reply to this topic.