Read-ValidInput.psm1
<#
.synopsis Offers the user options to select from and loops until they select a valid response. Simple E.g.: $Choice = Read-ValidInput -prompt "Continue?" -ValidInputs "y*","n*" .description Prompts the user for input, and runs each of your valid inputs against it. If any match, returns the user's input Options for retry limits, colours and wildcards E.g. proceed? (y/n): a Invalid input. proceed? (y/n): yes <passes back "yes"> .parameter Prompt The instruction to the user E.g. input: "proceed?" .parameter ValidInputs An array of strings - the valid things a user can type. If you put wildcards (*) here they will be respected and used, but not shown to the user Is shown to the user E.g. input: @(y*,n*) If getting this from a hashtable, or something with columns, only pass through a single column (using .ColumnName) E.g. $FolderContents = get-childitem Read-ValidInput -Prompt "Select the folder" -ValidInputs $FolderContents.Name .parameter MaxRetries How many times the user will be reprompted. Inclusive of the last try but not the first -1 to retry forever E.g. input: 3 results in an initial prompt, and then 3 prompts after that before exiting .parameter ForegroundColor Same as write-host - text colour E.g. input: Cyan .parameter BackgroundColor Same as write-host - text background colour E.g. input: Black .parameter Commas Whether to separate the options shown to the user with commas (and spaces) instead of slashes Without this switch it's slashes E.g. input: just specify it, its a switch. .parameter FormatWide For use with a large number of items. If specified, this shows the options in a more separate, wider neater list .parameter CaseSensitive Whether to only match input with the right case as well Without this switch its case insensitive E.g. input: just specify it, its a switch .notes Author: Ben Renninson Email: ben@goldensyrupgames.com From: https://github.com/GSGBen/powershell #> function Read-ValidInput { Param ( [Parameter(Mandatory=$true)][string]$Prompt, [Parameter(Mandatory=$true)][string[]]$ValidInputs, [Parameter(Mandatory=$false)][int]$MaxRetries = -1, [Parameter(Mandatory=$false)][ConsoleColor]$ForegroundColor, [Parameter(Mandatory=$false)][ConsoleColor]$BackgroundColor, [Parameter(Mandatory=$false)][switch]$Commas, [Parameter(Mandatory=$false)][switch]$FormatWide, [Parameter(Mandatory=$false)][switch]$CaseSensitive ) #No errors in this are worth continuing past $ErrorActionPreference = "Stop" #Prep the colour parameters. We'll only pass them if they've been set $ColorParameters = @{} if ($ForegroundColor) {$ColorParameters.add("ForegroundColor",$ForegroundColor)} if ($BackgroundColor) {$ColorParameters.add("BackgroundColor",$BackgroundColor)} #Prepare the options to show to the user. Hide * wildcards if ($Commas) {$JoinWith = ", "} else {$JoinWith = "/"} $OptionsString = $ValidInputs -join $JoinWith $OptionsString = $OptionsString.Replace("*","") #Start prompting after getting some variables ready to check $FoundMatch = $false $RetryCount = 0 #do/while runs at least once and checks at the end do { #Notify if they've incorrectly entered and we're retrying if ($RetryCount -gt 0) { write-host #formatting. Separate retries write-host "Invalid input. " -NoNewline @ColorParameters #Different selection nudge based on formatting type if ($FormatWide) { write-host "Enter a valid option from the list." @ColorParameters } else { write-host "Enter a valid option from within the brackets." @ColorParameters } } #The actual prompt if ($FormatWide) #wide formatting of options { #Prompt at the top to explain write-host "${Prompt}" -NoNewline @ColorParameters #Show the options. The last two pipes are for the colors $ValidInputs | format-wide {$_} -AutoSize -Force | out-string | write-host -NoNewline @ColorParameters #If it exists, mention the instruction underneath (where they'll be looking) again as well if ($RetryCount -gt 0) { write-host "Invalid input. Enter a valid option from the list." @ColorParameters } #Duplicate to where they'll be looking write-host "${Prompt}:" -NoNewline @ColorParameters } else { #${VariableName} instead of $VariableName is magic and explicity states the end of the variable name so you can type right after it write-host "${Prompt} (${OptionsString}):" -NoNewline @ColorParameters } #Get the input $Input = Read-Host #Check each validinput against the input for a match foreach ($Entry in $ValidInputs) { if ($CaseSensitive) #match, case sensitive { if ($Input -clike $Entry) { #Found a match, stop checking $FoundMatch = $true break } } else #match, case insensitive { if ($Input -like $Entry) { #Found a match, stop checking $FoundMatch = $true break } } } } #Only reprompt if we haven't found a match, and if the next run will be within the retry count #++ before the variable increments the $RetryCount count here within the check, before using it #Finally, don't worry about the count if it's -1 (loop forever) while ( (!$FoundMatch) -and ( (++$RetryCount -le $MaxRetries) -or ($MaxRetries -eq -1) ) ) #Finally return the input if we found a match at all if ($FoundMatch) {$Input} } |