0

I am attempting to create a menu for restoring backups. Ideally, the user will see a list of folders, each numbered and then be prompted to choose. The list and selection options will need to be dynamic.

The problem is my limited understanding of loops and arrays. Here is the array I am trying to work with:

$saved_backups = Get-ChildItem $backup_folder  |Where-Object {$_.Name -like "*$target_username"} | select-object @{n="Saved Backups:";e={$_.Name}}
[array]::Reverse($saved_backups)

From the user's perspective, I would like it to look like:

1) $saved_backups[0]
2) $saved_backups[1]

or

1) 2015.04.21_laptop
2) 2015.04.20_laptop

Where backup is actually a folder name. The user would then select the number and the selection saved to a variable I can use later.

Any help is appreciated!

EDIT #1: I was able to simplify the folder name needed with the following:

$saved_backups = Get-ChildItem $backup_folder  |Where-Object {$_.Name -like "*$target_username"}
[array]::Reverse($saved_backups)
$saved_backups = $saved_backups.name

EDIT #2: Thanks to Matt, I am starting to understand the idea of using the index. The problem now is with my array, as it is counting both objects as one, then counting it twice.

$saved_backups = Get-ChildItem $backup_folder |Where-Object {$_.Name -like "*$target_username"}
[array]::Reverse($saved_backups)
$saved_backups = $saved_backups.name
For($folderIndex = 1; $folderIndex -le $saved_backups.Count; $folderIndex++){
Write-Host "[$folderIndex]$saved_backups"
}

Current Output:

[1] Folder1 Folder2
[2] Folder1 Folder2

Ideally it would look like:

[1] Folder1
[2] Folder2

Once I understand the for statement, I will work on the selection menu.

2
  • Are you just looking for a loop that will number the entries? For this to have the most value can you include your choice structure/code? Commented Apr 21, 2015 at 19:54
  • Yes, I am not sure what type of loop will allow each found folder ($saved_backups) to first display a count and then display the respective value in the array. I need help with the choice structure, but was planning to include it in the loop. Commented Apr 21, 2015 at 20:08

3 Answers 3

1

I just whipped this up and it uses the choice code.

$backupLocations = get-childitem c:\temp -filter '*data*' | Where-Object{$_.PSIsContainer}

$title = "Backups"
$message = "Please choose a backup location"
$backupLocations.count
$selections = @()
For($folderIndex = 1; $folderIndex -le $backupLocations.Count; $folderIndex++){
    $selections += New-Object System.Management.Automation.Host.ChoiceDescription "&$folderIndex- $($backupLocations[$folderIndex -1].Name)", $backupLocations[$folderIndex -1].FullName
}
$options = [System.Management.Automation.Host.ChoiceDescription[]]($selections)
$result = $host.ui.PromptForChoice($title, $message, $options, 0) 


$backupLocations[$result].FullName

It will prompt the user to select a choice as defined by the above code. In interactive mode the prompt would look like this. Data and Data2 being folder names.

Choice

Where your actual question comes into play is looping the folders and numbering them. I am going to focus on that.

For($folderIndex = 1; $folderIndex -le $backupLocations.Count; $folderIndex++){
    $selections += New-Object System.Management.Automation.Host.ChoiceDescription "&$folderIndex- $($backupLocations[$folderIndex -1].Name)", $backupLocations[$folderIndex -1].FullName
}

So we use an index to step through the array. This index is only used so that we can count as we go. For each folder that is identified we create a choice or selection object. That object has a title and description. The ampersand associates the number to choice so the user can select it like a shortcut. In the picture above you will notice the underline below the numbers.

FYI

For you second edit you were close. $saved_backups is itself an array and you can index it as well. If you noticed that is what I did in my code samples.

For($folderIndex = 1; $folderIndex -le $saved_backups.Count; $folderIndex++){
Write-Host "[$folderIndex]$($saved_backups[$folderIndex-1])"
}

Before what was happening is the entire list of $saved_backups was being converted to a string so that it could be outputted which is why you saw the repetition.

Sign up to request clarification or add additional context in comments.

1 Comment

Very helpful, thank you! I was able to use the folder index idea and got it close. I will stick with a CLI menu for this iteration. Still having problems with the folderindex counting twice, but instead of each getting a number, everything gets counted twice. I will do a 2nd edit with my current code.
1

I feel obliged to chime in. I keep a function on hand to generate text menus, so I'm posting it here in case you want it.

Function MenuMaker{
    param(
        [parameter(Mandatory=$true,
        ValueFromPipeline = $true)][String[]]$Selections,
        [string]$Title = $null
        )

    $Width = if($Title){$Length = $Title.Length;$Length2 = $Selections|%{$_.length}|Sort -Descending|Select -First 1;$Length2,$Length|Sort -Descending|Select -First 1}else{$Selections|%{$_.length}|Sort -Descending|Select -First 1}
    $Buffer = if(($Width*1.5) -gt 78){[math]::floor((78-$width)/2)}else{[math]::floor($width/4)}
    if($Buffer -gt 6){$Buffer = 6}
    $MaxWidth = $Buffer*2+$Width+$($Selections.count).length+2
    $Menu = @()
    $Menu += "╔"+"═"*$maxwidth+"╗"
    if($Title){
        $Menu += "║"+" "*[Math]::Floor(($maxwidth-$title.Length)/2)+$Title+" "*[Math]::Ceiling(($maxwidth-$title.Length)/2)+"║"
        $Menu += "╟"+"─"*$maxwidth+"╢"
    }
    For($i=1;$i -le $Selections.count;$i++){
        $Item = "$(if ($Selections.count -gt 9 -and $i -lt 10){" "})$i`. "
        $Menu += "║"+" "*$Buffer+$Item+$Selections[$i-1]+" "*($MaxWidth-$Buffer-$Item.Length-$Selections[$i-1].Length)+"║"
    }
    $Menu += "╚"+"═"*$maxwidth+"╝"
    $menu
}

Then either just feed it an array or give it an array and a title.

$Saved_Backups = @("2015.04.21_laptop","2015.04.20_laptop")
menumaker $Saved_Backups
╔════════════════════════════╗
║    1. 2015.04.21_laptop    ║
║    2. 2015.04.20_laptop    ║
╚════════════════════════════╝

Or, add a title...

menumaker $Saved_Backups -Title "Select a backup to restore"
╔═════════════════════════════════════════╗
║       Select a backup to restore        ║
╟─────────────────────────────────────────╢
║      1. 2015.04.21_laptop               ║
║      2. 2015.04.20_laptop               ║
╚═════════════════════════════════════════╝

Want to get fancy and add selection validation?

$saved_backups = Get-ChildItem $backup_folder  |Where-Object {$_.Name -like "*$target_username"} | select-object @{n="Saved Backups:";e={$_.Name}}
[array]::Reverse($saved_backups)
$Options = [Ordered]@{}
$BkUpCnt = 1
$saved_backups | ForEach{$Options.Add($BkUpCnt,$_);$BkUpCnt++}
Do{
    menumaker $saved_backups -Title "Select a backup"
    If(!([string]::IsNullOrEmpty($Selection)) -and $Selection -notin $Options.keys){Write-Host "`nNot a valid selection, please try again.`n" -ForegroundColor Red}
    $Selection = Read-Host "Backup selection"}
While(([string]::IsNullOrWhiteSpace($Selection)) -or $Selection -notin $Options.keys)
$SelectedBackup = $Options[($Selection - 1)]

That displays the menu, prompts them for a selection, makes sure that what they selected is a valid option, and if not re-displays it adding in an error stating that they did not select a valid option.

Comments

0

Finally learned. My issue was applying the loop to each object in the variable, then figuring out I needed to bring the counter/$index outside the loop. Life is tough, but it's tougher if your stupid. =D

$saved_backups = Get-ChildItem $backup_folder |Where-Object {$_.Name -like "*$target_username"}
[array]::Reverse($saved_backups)
$saved_backups = $saved_backups.name

#This is my answer. The Counter/$Index outside the Loop.
$index = 0
foreach ($element in $saved_backups) {
$index++
Write-Host "[$index]$element"
}

Output:

[1] Folder1
[2] Folder2

PS - Matt will get an upvote as soon as I have the points. Thank you for the help.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.