$total_time
倒计时到 0,并显示进度条。这很好,但是如何在自定义按键(例如“ESC”、“Enter”、“Space”或“m”)时退出循环并继续执行脚本?我如何拥有多个自定义退出选项,例如“x”将退出循环并继续脚本,“p”将退出循环并执行一些任务(即捕获按下的键并测试其他任务),或者在倒计时期间进行其他操作?
$total_time = 17 # Seconds in total to countdown
$interval = $total_time / 100 # There are always 100 percentage pips
$ms_per_pip = $interval * 1000
For ($i=0; $i -le 100; $i++) { # Always 100 pips
Start-Sleep -Milliseconds $ms_per_pip
$remaining_time = [math]::Round($total_time - ($i * $ms_per_pip / 1000),2)
Write-Progress -Activity "Sleeping For $total_time Seconds ($i% complete, $remaining_time Seconds left)" -Status "StatusString" -PercentComplete $i -CurrentOperation "CurrentOperationString"
}
Console.ReadKey(bool)
获取按下的键,之后这是一个简单的 if
条件来检查按下的键是否可用在那些感兴趣的字符之一中表示循环的
break
。之后,您可以使用
switch
对按下的键进行操作。
for($i = 0; $i -le 100; $i++) {
if([Console]::KeyAvailable) {
$key = [Console]::ReadKey($true).Key
if($key -in 'X', 'P') {
break
}
}
# rest of code here
}
switch($key) {
X {
'X was pressed'
# do something with X
}
P {
'P was pressed'
# do something with P
}
}
但我只是想提供完全有效的独立示例脚本,方法有点不同。
在原始解决方案中,计时器可能会阻塞主 Gui 线程,因此在下面的示例中,我在单独的任务线程中进行了计时器倒计时,以便其他任务仍然可以在主线程中完成。
在原始解决方案中,按下正确的按键后,用户可能需要等待间隔完全过去,直到脚本从睡眠中唤醒并响应。对于较长的定时器持续时间,间隔可以变得更长(定时器总持续时间的 1%)。
我在下面的示例中进行了修改,以便用户可以在按下正确的按键后立即跳出计时器循环。
Function InterruptibleTimer([long] $timer_duration_in_seconds, [string] $status_string, [string] $current_operation_string)
{
[long] $timer_interval_in_seconds = 1;
[long] $timer_interval_in_milliseconds = $timer_interval_in_seconds * 1000;
[System.DateTime] $start_time = Get-Date;
$pressed_key = $null;
[System.Timers.Timer] $timer = New-Object System.Timers.Timer;
$timer.Interval = $timer_interval_in_milliseconds;
#$timer.AutoReset = $true; # default is $true to prevent timer from stopping after reaching first interval
[System.Collections.Hashtable] $timer_data =
@{
StartTime = $start_time;
TimerDurationInSeconds = $timer_duration_in_seconds;
StatusString = $status_string;
CurrentOperationString = $current_operation_string;
};
[System.Management.Automation.ScriptBlock] $timer_instruction_block =
{
$start_time = $Event.MessageData.StartTime;
$timer_duration_in_seconds = $Event.MessageData.TimerDurationInSeconds;
$status_string = $Event.MessageData.StatusString;
$current_operation_string = $Event.MessageData.CurrentOperationString;
[System.DateTime] $current_time = Get-Date;
[System.TimeSpan] $elapsed_time = $current_time - $start_time;
[long] $elapsed_seconds = [math]::Floor($elapsed_time.TotalSeconds);
[long] $remaining_seconds = $timer_duration_in_seconds - $elapsed_seconds;
[double] $elapsed_percentage = ($elapsed_seconds / $timer_duration_in_seconds) * 100;
Write-Progress -PercentComplete $elapsed_percentage -Status "$status_string" -CurrentOperation "$current_operation_string" -Activity "Timer duration in seconds: $timer_duration_in_seconds s, Elapsed seconds: $elapsed_seconds s, Remaining seconds: $remaining_seconds s, Elapsed percentage: $($elapsed_percentage.ToString("F2"))% complete";
};
$timer_task = Register-ObjectEvent -InputObject $timer -EventName Elapsed -Action $timer_instruction_block -MessageData $timer_data;
$timer.Start(); # Start the timer
Write-Host "Timer has been started";
try {
Write-Host "Awaiting user's key press or waiting until timer reaches its full duration"
while ($true) {
[System.DateTime] $current_time = Get-Date;
[System.TimeSpan] $elapsed_time = $current_time - $start_time;
[long] $elapsed_seconds = [math]::Floor($elapsed_time.TotalSeconds);
if($elapsed_seconds -ge $timer_duration_in_seconds) {
break;
}
if([System.Console]::KeyAvailable) {
$pressed_key = [System.Console]::ReadKey($true).Key;
Write-Host "Pressed key: $pressed_key";
if($pressed_key -in 'x', 'p', 'm', 'Enter', 'Spacebar', 'Escape') {
break;
} else {
Write-Host "To stop the timer now, press one of the following buttons:";
Write-Host "x, p, m, Enter, Space, Esc:";
}
}
}
} finally {
$timer.Stop();
Remove-Job $timer_task -Force;
Write-Host "Timer has been stopped";
switch -CaseSensitive ($pressed_key) {
$null { Write-Host 'No key was pressed'; }
'X' { Write-Host 'Exiting the loop and continuing the script'; }
'P' { Write-Host 'Exiting the loop and doing some tasks'; }
{ @('M', 'Enter', 'Spacebar', 'Escape') -contains $_ } { Write-Host 'Exiting the loop'; }
}
}
return $pressed_key;
}
[long] $TimerDurationInSeconds = 5 * 60; # 5 minutes timer's duration
[string] $StatusString = "Status String";
[string] $CurrentOperationString = "Current Operation String";
Write-Host "Starting the timer which will stop automatically after $TimerDurationInSeconds seconds";
Write-Host "To stop the timer now, press one of the following buttons:";
Write-Host "x, p, m, Enter, Space, Esc:";
$pressed_key = InterruptibleTimer -timer_duration_in_seconds $TimerDurationInSeconds -status_string $StatusString -current_operation_string $CurrentOperationString;