Versions of this question ("dynamic variables" or "variable variables" or "create variables at runtime") come up a lot, and in almost all cases they are not the right answer.
This is often asked by people who don't know a better way to approach their problem, but there is a better way: collections. Arrays, lists, hashtables, etc.
Here's the problem: You want to read a username and print it out. You can't write Hello Alice
because you don't know what their name is to put in your code. That's why variables exist:
$name = Read-Host "Enter your name"
Write-Host "Hello $name"
Great, you can write $name
in your source code, something which never changes. And it references their name, which does change. But that's OK.
But you're stuck - how can you have two people's names, if all you have is $name
? How can you make many variables like $name2
, $name3
? How can you make $Alice, $Bob
?
And you can...
New-Variable -Name (Read-Host "Enter your name") -Value (Read-Host "Enter your name again")
Write-Host "Hello
wait
What do you put there to write their name? You're straight back to the original problem that variables were meant to solve. You had a fixed thing to put in your source code, which allowed you to work with a changing value.
and now you have a varying thing that you can't use in your source code because you don't know what it is again.
It's worthless.
And the fix is that one variable with a fixed name can reference multiple values in a collection.
Arrays (Get-Help about_Arrays
):
$names = @()
do {
$name = Read-Host "Enter your name"
if ($name -ne '')
{
$names += $name
}
} while ($name -ne '')
# $names is now a list, as many items long as it needs to be. And you still
# work with it by one name.
foreach ($name in $names)
{
Write-Host "Hello $name"
}
# or
$names.Count
or
$names | foreach { $_ }
And more collections, like
Hashtables (Get-Help about_Hash_Tables
): key -> value pairs. Let's pair each file in a folder with its size:
$FileSizes = @{} # empty hashtable. (aka Dictionary)
Get-ChildItem *.txt | ForEach {
$FileSizes[$_.BaseName] = $_.Length
}
# It doesn't matter how many files there are, the code is just one block
# $FileSizes now looks like
@{
'readme' = 1024;
'test' = 20;
'WarAndPeace' = 1048576;
}
# You can list them with
$FileSizes.Keys
and
foreach ($file in $FileSizes.Keys)
{
$size = $FileSizes[$file]
Write-Host "$file has size $size"
}
No need for a dynamic variable for each file, or each filename. One fixed name, a variable which works for any number of values. All you need to do is "add however many there are" and "process however many there are" without explicitly caring how many there are.
And you never need to ask "now I've created variable names for all my things ... how do I find them?" because you find these values in the collection you put them in. By listing all of them, by searching from the start until you find one, by filtering them, by using -match
and -in
and -contains
.
And yes, New-Variable and Get-Variable have their uses, and if you know about collections and want to use them, maybe you do have a use for them.
But I submit that a lot of people on StackOverflow ask this question solely because they don't yet know about collections.
Dynamic variables in Powershell
Incrementing a Dynamic Variable in Powershell
Dynamic variable and value assignment in powershell
Dynamically use variable in PowerShell
How to create and populate an array in Powershell based on a dynamic variable?
And many more, in Python too:
https://stackoverflow.com/a/5036775/478656
How can you dynamically create variables via a while loop?