-
Notifications
You must be signed in to change notification settings - Fork 1
/
choice.ps1
151 lines (140 loc) · 4.89 KB
/
choice.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
Function Choice {
<#
.DESCRIPTION
Asks the user to choose from a configurable list of options.
Loops until a valid option has been chosen or the timout is reached.
Supports a default option to be chosen in case of timeout of user presses the Enter key.
Can also be used as a replace for the "pause" command ("Press any key to continue...")
.PARAMETER message
Specifies the message to be shown.
Allowed choices are automatically generated out of every single letter written between square brackets.
For example: "Continue? [Y]es or [N]o" makes Y and N acceptable choices (not case sensitive).
The first letter in square brackets preceeded by > is set as default value.
For example: "Continue? [Y]es or >[N]o" makes N the default choice when the timeout is reached or the user presses Enter.
When message contains no choices, every keypress is accepted and Enter as default choice.
The message can be a multi-line string:
choice "Continue?`n[Y] or >[N]o" -timeOutSeconds -1
.PARAMETER timeOutSeconds
The number of seconds until the timeout is reached.
0 means that the default choice is applied immediately.
-1 or any other value less than 0 disables the timeout.
.PARAMETER allowTypeAhead
When set to $false, the keyboard input buffer is cleared right before the message is displayed.
This is useful in scenarios where a script pops up while the user is working on something else and might just continue typing.
When set to $false, script automation via input redirection to a file ("<"-sign in common shells) stops at this point.
.INPUTS
The message can be piped:
"Continue?`n[Y] or >[N]o" | choice -timeOutSeconds -1
.OUTPUTS
String containing pressed valid key or default value, else $null.
.EXAMPLE
choice "Continue?`n[Y] or [N]o" -timeOutSeconds -1
"Continue?`n[Y] or >[N]o" | choice -timeOutSeconds -5
#>
param (
[parameter(ValueFromPipeline)]
[string]$message = $null,
[int]$timeOutSeconds = -1,
[bool]$allowTypeAhead = $true
)
$regexPattern = ($message | Select-String -Pattern '(?<=\[).(?=\])' -AllMatches).matches.value -join ''
If ($regexPattern) {
$regexPattern = "[$regexPattern]"
}
else {
$regexPattern = '.'
}
$defaultValue = ($message | Select-String '(?<=\>\[).(?=\])').matches.value -join ''
$key = $null
if (![string]::IsNullOrEmpty($message)) {
#Write-Host -NoNewline $message
$a = $message -split '(\>\[.\])', 2
for ($i = 0; $i -lt $a.count; $i++) {
if ($a[$i] -like '>`[J`]') {
Write-Host $a[$i] -NoNewline -ForegroundColor Green
#Write-Host $a[$i].substring(2, 1) -NoNewline -ForegroundColor Green
}
else {
$b = $a[$i] -split '(\[.\])'
for ($j = 0; $j -lt $b.count; $j++) {
if ($b[$j] -match '(\[.\])') {
Write-Host $b[$j] -ForegroundColor Yellow -NoNewline
#Write-Host $b[$j].substring(1, 1) -ForegroundColor Yellow -NoNewline
}
elseif ($b[$j] -ne '') {
Write-Host $b[$j] -NoNewline
}
}
}
}
if (-not $message.endswith(" ")) {
Write-Host " " -NoNewline
}
}
$queryDelay_ms = 250
$counter = $timeOutSeconds * (1000 / $queryDelay_ms)
if (-not $allowTypeAhead) {
while ($Host.UI.RawUI.KeyAvailable) {
$key = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown,IncludeKeyUp")
$key = $null
}
}
$origpos = $host.UI.RawUI.CursorPosition
$scroll = "|/-\"
$idx = 0
while ($null -eq $key -and ($timeOutSeconds -lt 0 -or ($counter-- -gt 0))) {
if (($timeOutSeconds -eq 0) -or $Host.UI.RawUI.KeyAvailable) {
$key_ = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown,IncludeKeyUp")
if ($key_.KeyDown -and (($key_.Character -match $regexPattern) -or ($key_.VirtualKeyCode -eq 13))) {
$key = $key_.Character
}
}
else {
if ($timeOutSeconds -gt 0) {
$tempString = ("(" + (('{0:d' + $timeOutSeconds.tostring().length + '}') -f [int]([Math]::ceiling(($counter * $queryDelay_ms) / 1000))) + ") ")
}
else {
<#
$tempstring = ($scroll[$idx] + ' ')
$idx++
if ($idx -ge $scroll.Length) {
$idx = 0
}
#>
}
if ($timeOutSeconds -gt 0) {
$host.UI.RawUI.CursorPosition = $origpos
write-host $tempString -NoNewline
Start-Sleep -m $queryDelay_ms
}
}
}
$host.UI.RawUI.CursorPosition = $origpos
write-host (' ' * $tempString.Length)
$host.UI.RawUI.CursorPosition = $origpos
if ($null -ne $key) {
if ($key_.VirtualKeyCode -eq 13) {
$key = $defaultValue
if ($defaultValue -eq '') {
Write-Host
}
}
else {
#Write-Host -NoNewline "$($key.Character)"
}
}
else {
$key = $defaultValue
if ($defaultValue -eq '') {
Write-Host
}
}
if (($null -eq $key) -or ($key -eq '')) {
Write-Host
return $null
}
else {
Write-Host $key
return $key
}
}