I would like to pass the string name of any Powershell variable to any function requiring a simple -Name parameter string e.g. $anyvar1 as a usable string with value 'anyvar1'. A function will use that variable's name literal var1 in this type of statement, which removes $var1:
param (
[string] $varName
)
try
{
Remove-Variable -Name $varName
}
catch{
# trap any message if the variable doesn't exist
}
}
I need this to be dynamic so if I pass any old variable to a function like this trivial example e.g.
RemoveVar $anyvar1
it will just get the name, not like this where a string literal is used. T
RemoveVar -Name '$anyvar1'
The second approach works, if the $ is stripped off the name literal and so on, but has be constructed.
So any function needing a simple -Name string parameter needs to get that name from the variable $anyvar1 object itself.
Get-Variable requires a simple name to get a variable as an object and thereby access any of its properties, including Name, but the chicken and egg situation occurs.
So a one-liner to get the object name property from any variable is needed. If it is of a particular scope, need to get the scope as well
So,
$Global:anyvar1="SOMEVALUE"
...
...
# all done with $Global:anyvar1
# figure out its name
$varName = Get-VarName -inputObject $Global:anyvar1 ??????
Remove-Variable -Name $varName -Scope Global
# $varName would contain the literal Global:anyvar1.
the function Get-VarName does not exist AFAICT - but there must be a way to
Obviously any locally declared variables, or script level, go out of scope and so on, but $Global variables and others of that sort may need to be explicitly removed.
In C#, I would probably have used nameof(anyvar1) to do this, so something simple like that?
Any help is appreciated. It is probably blindingly obvious but I am not getting it.
Santiago has already provided helpful pointers in the comments; let me elaborate:
PowerShell currently (PowerShell 7.4.x) has no equivalent to C#'s nameof
feature.
$varName
is a reference to the value of the variable named varName
, and therefore doesn't support reflecting on the variable itself.
The only exception is in assignments: in $varName = ...
$varName
indeed identifies the target variable.
As an aside: POSIX-compatible shells avoid this ambiguity by requiring that no $
be used in assignments (varName=...
).
The PowerShell-idiomatic way is indeed to pass the names of variables as a way to identify a variable object for other commands to operate on, via the *-Variable
cmdlets (e.g. Get-Variable
and Set-Variable
, which of necessity also operate on variable names).
Get-Variable -ValueOnly HOME
retrieves the value of the automatic $HOME
variable, and, likewise, the common -OutVariable
parameter expects the name of the output variable as its argument; e.g., -OutVariable out
captures the output in variable $out
.Therefore, I suggest you use the same approach, in combination with a custom argument-completer to make tab-completion of existing variable names easier.
Windows PowerShell solution, using an ArgumentCompleter
attribute for tab-completion in combination with a ValidateScript
for argument validation:
function Invoke-Foo {
param(
[Parameter(Mandatory)]
[ArgumentCompleter({
param($command, $parameter, $wordToComplete)
@((Get-Variable).Name) -like "$wordToComplete*"
})]
[ValidateScript({
if ($_ -notin (Get-Variable).Name) { throw "'$_' is not the name of an existing variable." }
$true
})]
[string] $VarName
)
# Sample output.
[pscustomobject] @{
VarName = $VarName
# Get the variable's value; note the indirection: the target
# variable name is specified via the value of *another* variable,
# $VarName
VarValue = Get-Variable -ErrorAction Ignore $VarName -ValueOnly
}
}
PowerShell (Core) 7+ solution, via a custom class
implementing IValidateSetValuesGenerator
, in combination with the ValidateSet
attribute, which combines tab-completion with argument validation:
class ExistingVariableNames : System.Management.Automation.IValidateSetValuesGenerator {
[string[]] GetValidValues() { return (Get-Variable).Name }
}
function Invoke-Foo {
param(
[Parameter(Mandatory)]
[ValidateSet([ExistingVariableNames])]
[string] $VarName
)
# Sample output.
[pscustomobject] @{
VarName = $VarName
VarValue = Get-Variable -ErrorAction Ignore $VarName -ValueOnly
}
}
Note:
If you type Invoke-Foo <tab>
, the names of existing variables are offered for completion, and Invoke-Foo H<tab>
offers all variables whose name starts with H
.
Additionally, passing the name of a non-existing variable is prevented.