Search code examples
powershellvariablespass-by-referencereadonlyvariable-names

get powershell variable name from actual variable


I am trying to figure out how to get the name of a powershell variable from the object, itself.

I'm doing this because I'm making changes to an object passed by reference into a function, so I don't know what the object will be and I am using the Set-Variable cmdlet to change that variable to read only.

# .__NEEDTOGETVARNAMEASSTRING is a placeholder because I don't know how to do that.

function Set-ToReadOnly{
  param([ref]$inputVar)
  $varName = $inputVar.__NEEDTOGETVARNAMEASSTRING
  Set-Variable -Name $varName -Option ReadOnly
}
$testVar = 'foo'
Set-ToReadOnly $testVar

I've looked through a lot of similar questions and can't find anything that answers this specifically. I want to work with the variable entirely inside of the function--I don't want to rely on passing in additional information.

Also, while there may be easier/better ways of setting read-only, I have been wanting to know how to reliably pull the variable name from a variable for a long time, so please focus solving that problem, not my application of it in this example.


Solution

  • Mathias R. Jessen's helpful answer explains why the originating variable cannot be reliably determined if you only pass its value.

    The only robust solution to your problem is to pass a variable object rather than its value as an argument:

    function Set-ToReadOnly {
      param([psvariable] $inputVar) # note the parameter type
      $inputVar.Options += 'ReadOnly'
    }
    
    $testVar = 'foo'
    Set-ToReadOnly (Get-Variable testVar) # pass the variable *object*
    

    If your function is defined in the same scope as the calling code - which is not true if you the function is defined in a (different) module - you can more simply pass just the variable name and retrieve the variable from the parent / an ancestral scope:

    # Works ONLY when called from the SAME SCOPE / MODULE
    function Set-ToReadOnly {
      param([string] $inputVarName)
      # Retrieve the variable object via Get-Variable.
      # This will implicitly look up the chain of ancestral scopes until
      # a variable by that name is found.
      $inputVar = Get-Variable $inputVarName
      $inputVar.Options += 'ReadOnly'
    }
    
    $testVar = 'foo'
    Set-ToReadOnly testVar # pass the variable *name*