You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 15:57:26