Skip to main content

A replacement for the at command which Microsoft has deprecated and removed in Windows 2012.

Set-StrictMode -Off;

$usage = "usage: runat (time) (command)

e.g. runat 2am shutdown -r -f -c ""restart""";

function esc($s) { [regex]::escape($s) }

$t = $args[0] # time

if($args.length -gt 1) { # command
  $c = $args[1..($args.length-1)] | % { if($_ -match '\s') { "`"$_`"" } else { $_ } }
  $c = [string]::join(' ', $c)
}

try { $d = get-date $t } # parse time
catch { echo "error: invalid time: '$t'"; $usage; exit 1 }

if($d -lt [DateTime]::Now) { $d = $d.AddDays(1) }
if($d -lt [DateTime]::Now) { echo "error: invalid time: '$t'"; $usage; exit 1 }

if(!$d -or !$c) { $usage; exit 1; }

# /query for unused task name (returns exit code 1 when not found)
$tn='';
for($i=1;$true;$i++) {
    try { & schtasks /query /tn "runat$i" 2>&1 | out-null } catch { }
    if($lastexitcode -eq 1) { $tn = "runat$i"; break }
}

# coerce system date format for schtasks, doubling-up 'd' and 'M'
$df = (get-culture).datetimeformat.shortdatepattern -replace '(?<![Md])(M|d)(?!\1)', '$1$1'

# create the task
& schtasks /create /ru system /rl highest /sc once /tn $tn /tr `"$($c -replace '"', '\"')`" /st $d.tostring("HH:mm") /sd $d.tostring($df)

if($lastexitcode -eq 0) { echo "Task will run '$c' at $d"; exit 0 }
exit 1;