Variable contents changed by function it is passed to - what am I forgetting?

This topic contains 2 replies, has 2 voices, and was last updated by Profile photo of JB Lewis JB Lewis 2 years ago.

  • Author
    Posts
  • #20368
    Profile photo of JB Lewis
    JB Lewis
    Participant

    I recently (re)wrote a coworker's script into a slightly more properly constructed function. One of the function's parameters takes an object, a collection/array of psobjects to be specific. The function does some filtering, and then tweaks the value of one property on certain elements of the collection. In testing, I'm importing a CSV to a variable, and then calling the function as such:

    $collection = $import-csv c:\temp\blah.csv
    get-hcpatchlists -list $collection
    

    I'm not sure I've written a script at all like this before and so I was quite surprised to find that after running the function, the array in $collection had been modified. As the title of the topic says, am I forgetting something fundamental?

    please forgive the gratuitous Write-Verboses. I was trying to figure out why the function was behaving differently the second time I ran it. I'm not a fan of what the script is doing in terms of output, but that's a battle for another day.

    function Get-HCPatchLists {
        [CmdletBinding()]
        param (
            # The object representing the list SIDv1
            [Parameter(Mandatory=$true,
                       ValueFromPipeline=$true,
                       Position=0)]
            [psobject]
            $list,
            # A path to save the files to
            [ValidateScript({Test-Path $_})]
            [string]
            $netpath = "\\myserver\somepath\ServerPatchList\"
        )
    
    begin {
    
        $excludes = ("Manual-Snowflakes","Linux","Shutdown Pending","Exempt","HCLIB","ESX Server","Departmental","Lab patching","Special Requirements","M 3,6,9,12")
        # Make sure that $netpath ends with a backslash
        If (-not ($netpath.endswith("\"))){
            $netpath += "\"   
        }
        write-verbose $netpath
    }
    process {
        # Filter out the patching schedules we don't want to deal with.
        $list = $list | Where-Object {$excludes -notcontains $_.PatchSchedule} | Where-Object {$_.Decommissioned -ne $true} 
        Write-Verbose "$($list.count) entries after filtering"
    
        foreach ($server in $list) {
            if ($server.PatchSchedule -eq "") {
                Write-Verbose "$($server.title) not categorized"
                $server.PatchSchedule = "ZZ-to be categorized"
            }
            if ($server.domain -eq "") {
                Write-verbose "De-nulling $($server.title)'s domain"
                $server.domain = "Unknown"
            }
     
            #Write-verbose "old domain: $($server.domain)"
            if ($server.domain -eq "ECOM") {
            #Write-Verbose $server.Cluster
    		    $server.cluster = ($server.Cluster).replace("Cluster","")
            #Write-Verbose $server.Cluster
    			$server.domain += "-$($server.cluster)"
    		}
            $path = "$netpath$($server.domain)-$($server.patchschedule).txt"
            try {
    		    #write-output "$name $patchstr"
    			out-file -filepath $path -append -inputobject $($server.title)
    		}
    		catch {
                write-verbose "error with $name : $_" 
            }
        }
    }
    }
    

    Is this expected behavior?

  • #20370
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    Yep, that's normal. There are two different ways you could attempt to modify a parameter. First, there's assigning to the variable itself:

    function Test-Something
    {
        param ($TheParameter)
    
        $TheParameter = 'A new value'
    }
    

    In this case, the "A new value" string would not be seen by the caller. However, another option is that you could modify the object referred to BY the parameter:

    function Test-Something
    {
        param ([hashtable] $Hashtable)
    
        $hashtable['SomeNewKey'] = 'Some New Value'
    }
    

    In this case, the caller would see the new key / value pair added to their hashtable. This is because the parameter variable inside the function and the variable that the caller passes to the function refer to the same object in memory, rather than a copy. This is true for all reference types (pretty much everything except for numeric values, Booleans, enums, with a few other exceptions that you may not run into in PowerShell.)

  • #20417
    Profile photo of JB Lewis
    JB Lewis
    Participant

    Thanks! That make sense!

    The simple solution is to leave the object alone, and perform the text manipulation with a variable that is only in the function's scope.

    Put another way, variables that "contain" objects, are really variables that "point to" objects.

You must be logged in to reply to this topic.