PowerShell: Unable to use a variable variable $Using:(Get-Variable -Name $Name)

148 views Asked by At

In a ForEach-Object -Parallel{}, I am trying to call variables with varying names defined prior to the function. This requires $Using:Name to pull in a variable from outside of the function.

However, when trying to use $Using:$Name or $Using:(Get-Variable -Name $Name) it provides an error: "Variable reference is not valid. ':' was not followed by a valid variable name character. Consider using ${} to delimit the name."

When trying to use ${Using:$Name}, {} gives the literal string and does not work. Other variations trying to make it work either provide the same variable reference is not valid or that the variable is null.

Variations tried:

$Using:{(Get-Variable -Name $Name)}
$Using:(Get-Variable -Name $Name)
Get-Variable -Name "Using:$Name"
Get-Variable -Name Using:$Name

Code Example:

$0IndexA = @(45,51,57)
$1IndexA = @(1490,1901,1903)

0..1 | Foreach-Object -Parallel {
    $ParallelNumber = $_ # Used for readability

    For ($c = 0; $c -le (Get-Variable $ParallelNumber"IndexA").value.count; $c++){
        Get-Variable -Name $ParallelNumber"IndexA"[$c]
    }
}

Error: Cannot find a variable with the name '0IndexA'.

Trying with -scope 1 Error:"The scope number '1' exceeds the number of active scopes. (Parameter 'Scope') Actual value was 1."

2

There are 2 answers

2
mklement0 On

You're looking for variable indirection, i.e. the ability to refer to a variable indirectly, via its name stored in another variable.

However, in the context of the $using: scope this is not supported: what follows : must be a literal variable name.

As Darin suggests, a workaround is to use a hashtable in the caller's scope that maps the input objects to the values to be used for them:

# Helper hashtable that maps each input to ForEach-Object -Parallel
# to values to use with that input.
$valuesHash = @{
  0 = @(45,51,57)
  1 = @(1490,1901,1903)
}

0..1 | Foreach-Object -Parallel {
  $values = ($using:valuesHash)[$_]
  "$values" # sample output; -> '45 51 57', '1490 1901 1903'
}

Note the need to enclose the $using: reference in (...) in order to apply an index ([$_]) to it.[1]


[1] Arguably, this shouldn't be necessary, but is as of PowerShell 7.3.9 - see GitHub issue #10876 for a discussion.

0
zdan On

@mkelement answer seems like a good solution. In case that doesn't work for you , another workaround is to use jobs instead of Foreach-Object -Parallel, that way you can pass the variables as input arguments.

I have re-worked your sample to demonstrate:

$0IndexA = @(45,51,57)
$1IndexA = @(1490,1901,1903)


$parJobs = 0..1 | foreach {
    Start-Job { 
    Param($0IndexA, $1IndexA, $ParallelNumber)
        For ($c = 0; $c -le (Get-Variable "$($ParallelNumber)IndexA").value.count; $c++){
            (Get-Variable -Name "$($ParallelNumber)IndexA").value[$c]
        }
    } -ArgumentList $0IndexA,$1IndexA, $_
} 

Wait-Job $parJobs | Receive-Job