I wanted to prepare a .BAT
file to automatically shut down the computer after a desired time.
And according to the information I got, I was able to create a code directory like the one below.
But at some point it doesn't accomplish anything I want.
@echo off
setlocal
set /p hours=Please enter the number of hours to shut down your computer (leave blank for only minutes):
set /p minutes=Please enter the number of minutes to shut down your computer:
set total_minutes=0
if defined hours set /a total_minutes=%hours% * 60
set /a total_minutes+=minutes
REM Check if there is an active shutdown request
for /F "tokens=2,3,4 delims=:." %%a in ('shutdown /a 2^>nul ^| findstr /C:"time remaining"') do (
set /a remaining_minutes=%%a*60+%%b
set /a elapsed_minutes=%total_minutes% - %remaining_minutes%
set /a new_total_minutes=%total_minutes% - %elapsed_minutes%
)
REM Display active shutdown request countdown if applicable
if defined remaining_minutes (
echo There is a shutdown request currently active.
echo The duration of the shutdown request is %remaining_minutes% minutes, and %elapsed_minutes% minutes have passed since the shutdown request was created.
echo %remaining_minutes% minutes remaining until shutdown.
echo Press Ctrl+C to cancel the shutdown.
echo.
choice /T 60 /C CN /D C > nul
if errorlevel 2 (
echo Continuing with the shutdown.
goto SHUTDOWN
) else (
echo Shutdown canceled.
exit /b
)
)
REM Display initial countdown information if no active shutdown request
echo Your computer will be shut down in %total_minutes% minutes.
echo Press Ctrl+C to cancel the shutdown.
echo.
choice /T 60 /C CN /D C > nul
if errorlevel 2 (
echo Continuing with the shutdown.
goto SHUTDOWN
) else (
echo Shutdown canceled.
exit /b
)
:SHUTDOWN
shutdown /s /t 0
1) When we run the bat file as "Administrator"
, we first encounter a screen asking how many hours we want the computer to be shut down.
When we leave this part blank, we can log in directly in minutes. Or by answering "1"
to the first question and "45"
to the second question, we can set the computer to shut down after "1 Hour and 45 Minutes"
.
However, when I want to add a countdown that shows the current screen to be turned off in 60 seconds
after entering this information, I cannot do it.
It needs to give us a 60 second countdown to cancel the current transaction; but I can't see any countdown on the screen. And the screen turns off automatically after 60 seconds.
2) When I run the .BAT
File again, I want it to check if there is an active shutdown request and present it to us.
However, when I run the file, it does not present this information to us.
I tried to do this; but it didn't work. If there is an existing shutdown request, it should specify each time the .BAT file is run.
Example: There is an active shutdown request currently set to 95 minutes
. This request was created 15 minutes
ago and has 80 minutes
until the computer shuts down.
If you want to cancel this request, please press CTRL+C
.
I don't know if this is possible; However, I think that the .BAT file can create a .TXT
file in the %TEMP%
folder and present us whether there is an active shutdown and the remaining time according to the information in that .TXT file.
Or another way.
If there is someone who has knowledge on this subject, I would like him to know that I am expressing my gratitude with all my sincerity.
Since you also tagged your question powershell, find a PowerShell solution below.
As for what you tried:
Despite calculating the appropriate amount of seconds to wait until initiating a shutdown, your batch file waits a fixed number of seconds, 60
, and then initiates a shutdown right away.
Passing the full delay period to choice.exe's /t
parameter - which would have to be expressed in seconds, limits you to at most 9999
seconds, i.e. less than 3 hours.
If, by contrast, you want to schedule a shutdown that is enforced independently of your batch file, you must use shutdown.exe
's /t
parameter and pass it the appropriate value in seconds (the limit is much higher, 315359999
(even though the documentation states 315360000
), which is ca. 10 years(!)).
After scheduling, you're then free to exit the batch file, but you'll have to rely on the system's notifications to warn the user of an impending shutdown, which - as of Windows 11 - work as follows:
600
seconds or fewer), a modal, centered-on-screen pop-up is shown; that is, the user has to acknowledge this message in order to continue working; no further notification is shown.601
seconds or more), a transient, non-modal notification is shown in the bottom-right corner of the screen (it disappears after ca. 5 seconds)
660
seconds or more) do you also get a modal pop-up once the shutdown is only 10 minutes away.There is apparently no API for detecting whether a shutdown (or restart) has been scheduled via shutdown.exe
's /t
parameter and for when.
The best you can do is to determine whether there is a pending shutdown, as shown in this SuperUser.com answer.
Yes, you could try to persist the expected shutdown time based on your most recent scheduling attempt in a text file, but note that there's no guarantee that this information will still apply when you call the batch file again, given that someone may have called shutdown
directly in the meantime (to implement a different delay or to cancel the shutdown altogether).
Since avoiding confusing GUI notifications is probably more important than a potential false positive, the solution below relies on the information in the text file alone.
Important: On Windows 11, every (successful) call to shutdown.exe
triggers a GUI notification, and the pop-up notifications described above will also be shown as the shutdown time approaches.
The solution below creates a synchronous countdown based on the future shutdown time specified by the user via a timespan from now, expressed either in minutes or hours and minutes.
If scheduling the shutdown succeeds, the shutdown timestamp (the point in time implied by the specified timespan) is persisted in a file, and a real-time countdown starts.
On aborting this countdown via Ctrl-C, the shutdown is aborted (canceled), and the script terminates.
Note: Handling Ctrl-C via the finally
block of a try
/ catch
/ finally
statement, as used below, restricts to you to cleanup operations - your script will invariably terminate.
If you need to intercept Ctrl-C so that you can opt to prevent termination, you'll need a custom keyboard-polling loop with [Console]::TreatControlCAsInput = $true
, as shown in this answer.
However, closing the window or otherwise forcefully terminating the PowerShell session (e.g. via Task Manager) will not abort the shutdown.
On re-executing the script after forcefully terminating the original call and/or while the original call is still running (in a different window):
If the persisted file exists and indicates a future point in time, it is assumed to be the real scheduled shutdown time - even though it isn't guaranteed to be, given that there's no way to query the system for this information.
In that case, no prompt is shown and the real-time countdown is resumed (again with the option to cancel the pending shutdown with Ctrl-C).
# Determine the path to a file in which information about a pending
# shutdown is persisted by this script.
$lastScheduleFile = Join-Path $env:TEMP ('~{0}_Schedule.txt' -f [IO.Path]::GetFileNameWithoutExtension($PSCommandPath))
[datetime] $shutdownTime = 0
# First, see if this script previously scheduled a shutdown.
try {
$shutdownTime = ([datetime] (Get-Content -ErrorAction Ignore $lastScheduleFile)).ToUniversalTime()
}
catch {}
# If the time is in the past, by definition it doesn't reflect the true pending shutdown time, so we ignore it.
if ($shutdownTime -lt [datetime]::UtcNow) {
$shutdownTime = 0
}
else {
# Warn that the retrieved shutdown time isn't *guaranteed* to be correct.
Write-Warning @'
The pending shutdown time is assumed to be what *this* script last requested,
which is not guaranteed to be the true time, nor is it guaranteed that a shutdown is even still pending.
'@
}
$shutdownAlreadyPending = $shutdownTime -ne 0
if (-not $shutdownAlreadyPending) {
# Prompt the user for when (how many minutes / hours and minutes from now) to shut down.
while ($true) {
try {
$secsFromNow = switch -Regex ((Read-Host 'Enter the timespan after which to shut down, either in minutes (e.g. 30) or hours and minutes (e.g. 1:15)').Trim()) {
'^[1-9]\d*$' { [int] $_ * 60; break }
'^\d+:\d+$' { ([timespan] $_).TotalSeconds; break }
default { throw }
}
break # input was valid; proceed below.
}
catch {
Write-Warning 'Invalid timespan entered; please try again.'
}
}
# Calculate the resulting shutdown time.
$shutdownTime = [datetime]::UtcNow.AddSeconds($secsFromNow)
# Schedule the shutdown via shutdown.exe
while ($true) {
# Note: Due to use of /t with a nonzero value, /f is implied,
# i.e. the shutdown will be forced at the implied time.
shutdown /s /t $secsFromNow
if ($LASTEXITCODE -eq 1190) {
# A shutdown/restart is already scheduled. We cannot know what its delay is.
Write-Warning "A shutdown is already pending. It will be canceled and rescheduled as requsted."
shutdown /a # Abort the pending shutdown, so that the new one can be requested as scheduled.
continue
}
break
}
if ($LASTEXITCODE) {
# Unexpected error.
Write-Error 'Scheduling a shutdown failed unexpectedly.'
exit $LASTEXITCODE
}
# Persist the scheduled shutdown time in a file, so that
# if this script gets killed, we can resume the countdown on re-execution.
$shutdownTime.ToString('o') > $lastScheduleFile
}
# Show a countdown display or handle a preexisting shutdown request,
# with support for Ctrl-C in order to cancel.
$ctrlCPressed = $true
try {
[Console]::CursorVisible = $false
# Display a countdown to the shutdown.
do {
$timespanRemaining = $shutdownTime - [datetime]::UtcNow
Write-Host -NoNewline ("`r" + 'SHUTTING DOWN in {0:hh\:mm\:ss}, at {1}. Press Ctrl-C to CANCEL.' -f $timespanRemaining, $shutdownTime.ToLocalTime())
Start-Sleep -Seconds 1
} while ($timespanRemaining -gt 0)
# Getting here means that Ctrl-C was NOT pressed.
$ctrlCPressed = $false
}
finally {
# Note: Only Write-Host statements can be used in this block.
[Console]::CursorVisible = $true
if ($ctrlCPressed) {
# Abort the pending shutdown.
shutdown /a *>$null
switch ($LASTEXITCODE) {
0 { Write-Host "`nShutdown aborted by user request." }
1116 { Write-Host "`n(Shutdown has already been canceled.)" }
default { Write-Host "`nUNEXPECTED ERROR trying to cancel the pending shutdown."; exit $_ }
}
}
# Clean up the file in which the last schedule attempt is persisted.
Remove-Item -ErrorAction Ignore $lastScheduleFile
# Note: We consider this way of exiting successful.
# If the shutdown is allowed to take place, this script never returns to a caller.
# If it *does* return:
# * If it is due to a *failure to even schedule* the shutdown (see above), it will be nonzero.
# * 0 therefore implies having successfully aborted (canceled) the shutdown.
exit 0
}