PowerShell定时关机脚本优化:Ctrl-C交互与终端逻辑调整
优化后的延迟关机PowerShell脚本
针对你提出的两个优化需求,以下是修改后的脚本,解决了Ctrl-C确认取消和终端保留的问题:
# 定义存储关机任务记录的文件路径 $lastScheduleFile = Join-Path $env:TEMP ('~{0}_Schedule.txt' -f [IO.Path]::GetFileNameWithoutExtension($PSCommandPath)) [datetime] $shutdownTime = 0 # 检查是否存在之前的关机任务记录 try { $shutdownTime = ([datetime] (Get-Content -ErrorAction Ignore $lastScheduleFile)).ToUniversalTime() } catch {} # 如果记录的时间已过期,则忽略该记录 if ($shutdownTime -lt [datetime]::UtcNow) { $shutdownTime = 0 } else { Write-Warning @" 检测到之前的关机任务记录,该时间不保证完全准确,也不保证关机任务仍在执行。 "@ } $shutdownAlreadyPending = $shutdownTime -ne 0 if (-not $shutdownAlreadyPending) { # 提示用户输入关机延迟时间 while ($true) { try { $secsFromNow = switch -Regex ((Read-Host '输入关机延迟时间,可输入分钟数(如30)或小时:分钟(如1:15)').Trim()) { '^[1-9]\d*$' { [int] $_ * 60; break } '^\d+:\d+$' { ([timespan] $_).TotalSeconds; break } default { throw } } break # 输入有效,继续执行 } catch { Write-Warning '输入格式无效,请重新输入。' } } # 计算关机时间 $shutdownTime = [datetime]::UtcNow.AddSeconds($secsFromNow) # 调用shutdown.exe设置关机任务 while ($true) { shutdown /s /t $secsFromNow if ($LASTEXITCODE -eq 1190) { Write-Warning "已有关机任务在执行,将取消原任务并重新设置。" shutdown /a # 取消现有关机任务 continue } break } if ($LASTEXITCODE) { Write-Error '设置关机任务失败。' exit $LASTEXITCODE } # 将关机时间写入记录文件,以便脚本重启后恢复倒计时 $shutdownTime.ToString('o') > $lastScheduleFile } # 显示倒计时并处理Ctrl-C确认逻辑 [Console]::CursorVisible = $false [Console]::TreatControlCAsInput = $true try { do { # 检测Ctrl-C输入 if ([Console]::KeyAvailable) { $key = [Console]::ReadKey($true) if ($key.Modifiers -eq 'Control' -and $key.Key -eq 'C') { Write-Host "`n" $confirm = Read-Host "确定要取消当前关机倒计时吗(Y/N)?" if ($confirm.Trim().ToUpper() -eq 'Y') { # 取消关机任务 shutdown /a *>$null switch ($LASTEXITCODE) { 0 { Write-Host "`n关机已取消。" } 1116 { Write-Host "`n(关机已经被取消过了。)" } default { Write-Host "`n取消关机时发生未知错误。"; exit $_ } } # 删除关机任务记录文件 Remove-Item -ErrorAction Ignore $lastScheduleFile # 询问是否重新设置关机 $newSchedule = Read-Host "是否要重新设置关机倒计时(Y/N)?" if ($newSchedule.Trim().ToUpper() -eq 'Y') { # 重启脚本进入设置流程 & $MyInvocation.MyCommand.Definition exit 0 } else { Write-Host "`n脚本结束。" exit 0 } } else { # 继续倒计时,清除当前行并恢复显示 Write-Host "`r" -NoNewline continue } } } $secsRemaining = ($shutdownTime - [datetime]::UtcNow).TotalSeconds if ($secsRemaining -le 0) { Write-Host "`n关机任务即将执行。" break } $timespanRemaining = $shutdownTime - [datetime]::UtcNow Write-Host -NoNewline ("`r" + '将在 {0:hh\:mm\:ss} 后关机,关机时间:{1}。按Ctrl-C取消。' -f $timespanRemaining, $shutdownTime.ToLocalTime()) Start-Sleep -Milliseconds 500 # 缩短休眠时间,更快响应Ctrl-C } while ($true) } finally { [Console]::CursorVisible = $true [Console]::TreatControlCAsInput = $false # 关机任务正常执行后删除记录文件 if ($secsRemaining -le 0) { Remove-Item -ErrorAction Ignore $lastScheduleFile } }
关键修改说明
- Ctrl-C确认提示:通过
[Console]::TreatControlCAsInput = $true将Ctrl-C转为可捕获的输入,在倒计时循环中检测到后弹出确认对话框,仅当用户输入Y时才执行关机取消操作 - 保留终端并支持重新设置:用户选择不取消(N)时,直接回到倒计时流程;取消关机后询问是否重新设置,选择Y则重启脚本进入设置环节,避免终端自动关闭;移除了原脚本中强制退出的逻辑,让流程更灵活
内容的提问来源于stack exchange,提问作者Albie
相关产品推荐
相关产品推荐

