Search code examples
powershell

How do I automatically create and use variable names?


I have 10 variables, called rows($row1, $row2, $row3...), I need the user to choose from one of this variables only knowing the right row, I'm struggling to understand how to connect text to a variable, that corrisponds to another variable and get the value, not to write it as text

$ChoosenRow = Read-Host
$rownumber = $("row" + $ChoosenRow)
$IP = Write-Output $rownumber 

If I input 10, I get $IP = row10 I'm trying to get $IP to become the value of $row10, not as a string.

Sorry for my broken english and my stupid question, I tried to search similar questions but didn't found any

Edit: iRon asked me to put more of the script:

# Carica il file e crea le variabili per il file
$FilePath = '\\ntced\CED\AssistenzaPC\IP IN GESTIONE\2021-08-16 TabellaIP.xls'
$xl = New-Object -ComObject Excel.Application
$xl.Visible = $true
$wb = $xl.Workbooks.Open($filepath)

# Prendi la prima colonna
$data = $wb.Worksheets['Foglio1'].UsedRange.Rows.Columns[1].Value2
$data2 = $wb.Worksheets['Foglio1'].UsedRange.Rows.Columns[2].Value2

# Chiudi excel aperto per prendere i dati
$wb.close()
$xl.Quit()
While([System.Runtime.Interopservices.Marshal]::ReleaseComObject($wb) -ge 0){}
while([System.Runtime.Interopservices.Marshal]::ReleaseComObject($xl) -ge 0){} 
Remove-Variable xl,wb # Rimuovi le due variabili create

# display results
$data | select -skip 1 # Rimuove il titolo sopra

# Variabili degli IP

$variables = @()
$i = 0
foreach ($row in ($data -split [Environment]::NewLine)) {
  # Crea una variabile per ogni riga
  New-Variable -Name "row$i" -Value $row
  # e un membro dell'array
  $variables += [pscustomobject]@{Name = "row$i"; Value = $row} # Copiato male, era troppo complicato arrivarci
  $i++
}

$variables2 = @()
$ii = 0
foreach ($linea in ($data2 -split [Environment]::NewLine)) {
  # Crea una variabile per ogni riga
  New-Variable -Name "linea$ii" -Value $linea
  # e un membro dell'array
  $variables2 += [pscustomobject]@{Name = "linea$ii"; Value = $linea} 
  $ii++
}

function Show-Menu
{
 param (
 [string]$Title = 'Scegli IP'
 )
 Clear-Host
 Write-Host "================ $Title ================"
 Write-Host "1: Cambia IP da tabella IP nuovi"
 Write-Host "Q: premi 'Q' per uscire."
}

Show-Menu –Title 'Scegli IP'
$selection = Read-Host 'Scegli la'

switch ($selection)
 {
     '1' {
         ' Scrivi la linea che contiene IP libero'

         $LineaScelta = Read-Host
         $rownumber = $("row" + $LineaScelta)

          $IP = Write-Output $rownumber
          $MaskBits = 20 # subnet mask in bits = 255.255.240.0
          $Gateway = "172.16.111.254"
          $Dns = "172.16.16.1"
          $IPType = "IPv4"
     } 

     'q' { 
           return
         }
 }

  # Imposta su tutte le schede di rete attive, quindi tenere acceso solo quella necessaria!
$adapter = Get-NetAdapter | ? {$_.Status -eq "up"}

# Rimuove gli IP prima di metterli 
If (($adapter | Get-NetIPConfiguration).IPv4Address.IPAddress) {
 $adapter | Remove-NetIPAddress -AddressFamily $IPType -Confirm:$false
}

If (($adapter | Get-NetIPConfiguration).Ipv4DefaultGateway) {
 $adapter | Remove-NetRoute -AddressFamily $IPType -Confirm:$false
}

 # Configura gli IP, CHE BESTEMMIE PER CAPIRE COME USARE

$adapter | New-NetIPAddress `
 -AddressFamily $IPType `
 -IPAddress $IP `
 -PrefixLength $MaskBits `
 -DefaultGateway $Gateway

# Configura il primo server DNS, non so come configurare il secondo ancora.
$adapter | Set-DnsClientServerAddress -ServerAddresses $DNS





Solution

  • General (best practice) advice:

    Don't use the <verb>-Variable cmdlets for dynamic variable names!

    (Rule request: #1706 Warning if Set/Get-Variable is invoked with only the basic -Name/-Value parameters.)

    Background

    PowerShell (as almost all other programming languages) has an internal dictionary were it keeps all the variable names and a (direct or indirect) reference to their containing value. This dictionary (quote wikipedia),

    is a data structure that implements a set abstract data type, a structure that can map keys to values. A hash table uses a hash function to compute an index, also called a hash code, into an array of buckets or slots, from which the desired value can be found. During lookup, the key is hashed and the resulting hash indicates where the corresponding value is stored.

    Why not?

    It is a general usage to create any (unique) variable name for a specific value, but at the moment that you find out that you start repeating yourself for a specific variable set, your program starts to become so called WET (as opposed to DRY). At this point you might come to the obvious conclusion to automate the variable names itself with statements using Set-Variable, like:

    New-Variable -Name "row$i" -Value $row
    

    To automate variables with a common purpose is definitely a good thought, but using <verb>-Variable cmdlets for this, is a bad practice as all the variables will eventually end up in the same dictionary. Therefore you might lose the oversight or even introduce conflicts with other variable names. Instead, you better create your own custom dictionary using a hashtable which is based on a similar data structure as the general variables.

    How?

    Instead of this:

    $variables = @()
    $i = 0
    foreach ($row in ($data -split [Environment]::NewLine)) {
      # Crea una variabile per ogni riga
      New-Variable -Name "row$i" -Value $row
      # e un membro dell'array
      $variables += [pscustomobject]@{Name = "row$i"; Value = $row} # Copiato male, era troppo complicato arrivarci
      $i++
    }
    

    You might simply do this:

    $Rows = @{}
    $i = 0
    foreach ($row in ($data -split [Environment]::NewLine)) { $Rows[$i++] = $Row }
    

    And to get the specific value:

    $LineaScelta = Read-Host
    $IP = $Rows[[Int]$LineaScelta]
    

    As the key of the dictionary appears to be a consecutive zero-based numeric serie, you might even simplify this further using an array:

    $Rows = $data -split [Environment]::NewLine
    
    $LineaScelta = Read-Host
    $IP = $Rows[$LineaScelta]
    

    As a side node: also try I avoid using the increase assignment operator (+=) to create a collection