问题
I am writing a script to check mail and I want it to check the unread mail every 20 or 30 minutes how can I schedule this task to run every 30 minutes using powershell.
回答1:
Here's a sample Powershell script which creates a scheduled task which can run with admin privileges
passing argument
-RunLevel Highest
makes it run as administrator, you may need to provide valid username/password which has admin right on the machine
$frequencyInMin = 20
$taskAction = New-ScheduledTaskAction -Execute 'Powershell.exe' `
-Argument '-NoProfile -WindowStyle Hidden -command "& {get-eventlog -logname Application -After ((get-date).AddDays(-1)) | Export-Csv -Path c:\temp\logs.csv -Force -NoTypeInformation}"'
$trigger = New-ScheduledTaskTrigger `
-Once `
-At (Get-Date) `
-RepetitionInterval (New-TimeSpan -Minutes $frequencyInMin) `
-RepetitionDuration (New-TimeSpan -Days (365 * $frequencyInMin))
Register-ScheduledTask -Action $taskAction -Trigger $trigger -TaskName "MyTasks\AppLog" -Description "Daily App Logs" -RunLevel Highest
回答2:
You didn't specified a version of PowerShell or the operation system under which you want to check for your emails.
For Windows:
The Task Scheduler in Windows has so many problems that I don't know where to begin.
Unfortunately, you can schedule a task which re-occurs every 20-30 min easily. You have to schedule a daily task at 00:00, 00:30, 01:00 etc. in cycle, so you would end up with 48 or 72 scheduled tasks in your scheduler. 🤔
If we are talking about Windows on notebooks, laptops even desktop PCs with UPS backup, you can't rely on the task scheduler. The tasks are not executed according the schedule because battery management interfere.
Another problem is that every 20-30 min a window of PowerShell will popup on your screen for a brief moment. You can suppress this by register the task as system service => under system service account. Other approach is to run PowerShell in CMD and hand over the script which must be executed. This is not an option if you don't have an admin rights.
Another, very tricky problem is that use of one of the must include switch (technically, you don't have to use this switch but the task gets executed faster) for scheduled PowerShell tasks: the -NonInteractive
switch will actively stop you from using some of the interactive cmdlets like Pause
, Read-Host
or Get-Credential
in your script which you want to execute. You have to design your scheduled task according the instruction which you want to execute in PowerShell session.
Not ideal option is the schedule the trigger as -Once
and setup this trigger with repetition:
$frequencyOfCheckInMin = 20
$howManyYearYouWantToCheckForEmails = 5
$params = @{
Once = $true
At = (Get-Date)
RepetitionInterval = (New-TimeSpan -Minutes $frequencyOfCheckInMin)
RepetitionDuration = (New-TimeSpan -Days ($howManyYearYouWantToCheckForEmails * 365 * $frequencyOfCheckInMin))
}
$trigger = New-ScheduledTaskTrigger @params
Disadvantages are:
- You have to know how long you want to schedule it for.
- Take leap years into account.
A better approach would be to register one task with PowerShell script that gets executed in 30 min for the task registration time manually. Your PowerShell script would contain a logic for an email check and update of the scheduled task trigger to a new, 30 min later from the time of the execution of the scheduled task trigger. Code follows.
# A definition of a general task which takes a script as argument and execute task
$pathToScript = 'D:\Fortis Backup\Test.ps1'
$ScheduledTaskActionParams = @{
Execute = "PowerShell.exe"
Argument = "-NoLogo -NoProfile -NonInteractive -File `"$pathToScript`""
}
$registerTaskParameters = @{
Principal = New-ScheduledTaskPrincipal -UserId "$ENV:USERDOMAIN\$ENV:USERNAME"
Trigger = New-ScheduledTaskTrigger -Once -At ((Get-Date).AddMinutes(30))
TaskName = 'Check email'
Action = New-ScheduledTaskAction @ScheduledTaskActionParams
}
Register-ScheduledTask @registerTaskParameters -Force
Your script
Get-EmailStatus
$newTrigger = New-ScheduledTaskTrigger -Once -At ((Get-Date).AddMinutes(30))
Set-ScheduledTask -TaskName 'Check email' -Trigger $newTrigger
Advantages:
- You can implement a logic for upper boundary or even skipping some days.
- You can simply turn on/off this task with other PowerShell scripts or just cmdlets.
Disadvantages:
- You have to handle Daylight saving time changes.
For details about the trigger, have a look over here.
For Linux (PowerShell Core):
It is a super simple.
- Install PowerShell Core.
- Make your script with check email logic.
- Schedule execution in CRON, e.g. */20 * * * * /root/powershell/7/pwsh /root/CheckEmail.ps1
More robust solution for Windows Task Scheduler:
To tackle problems with non-interactive cmdlets, I've designed these functions which I'm using in my private Swiss Army Knife Module for various PowerShell task.
function Confirm-PowerShellScriptForNonInteractiveMode
{
[CmdletBinding(PositionalBinding)]
Param (
# A path to PS1 file to test
[Parameter(
Mandatory,
ValueFromPipelineByPropertyName,
ValueFromPipeline,
Position = 0)]
[ValidateNotNullOrEmpty()]
[ValidateScript( { Test-Path $_ })]
[ValidateScript( { $_ -like '*.ps1' })]
[System.IO.FileInfo]
$Path
)
Begin
{
Write-Information 'Starting to validate script(s)...'
}
Process
{
$nonInteractiveCmdlets = @('Pause', 'Read-Host', 'Get-Credential')
$isValid = [System.Boolean]$true
$content = Get-Content -Path $Path
foreach ($cmdlet in $nonInteractiveCmdlets)
{
Write-Verbose "Script status: $isValid, testing $cmdlet"
$isValid = $isValid -AND !($content -match "$cmdlet")
}
return $isValid
}
End
{
Write-Information 'All scripts has been validated.'
}
}
function Register-PowerShellScheduledTask
{
[CmdletBinding(PositionalBinding)]
Param (
# A path to PS1 file to register
[Parameter(
Mandatory,
ValueFromPipelineByPropertyName,
ValueFromPipeline,
Position = 0)]
[ValidateNotNullOrEmpty()]
[ValidateScript( { Test-Path $_ })]
[ValidateScript( { $_ -like '*.ps1' })]
[ValidateScript( { Confirm-PowerShellScriptForNonInteractiveMode $_ })]
[System.IO.FileInfo]
$Path,
[Parameter(
ValueFromPipeline,
ValueFromPipelineByPropertyName,
Position = 1)]
[ValidateNotNullOrEmpty()]
[Microsoft.Management.Infrastructure.CimInstance]
$TaskTrigger = (New-ScheduledTaskTrigger -AtLogOn)
)
Begin
{
Write-Information 'Starting to register task(s)...'
}
Process
{
$ScheduledTaskActionParams = @{
Execute = "PowerShell.exe"
Argument = "-NoLogo -NoProfile -NonInteractive -File `"$Path`""
}
$registerTaskParameters = @{
Principal = New-ScheduledTaskPrincipal -UserId "$env:USERDOMAIN\$env:USERNAME"
Trigger = $TaskTrigger
TaskName = "Run $(Split-Path -Path $Path -Leaf) by PowerShell"
Action = New-ScheduledTaskAction @ScheduledTaskActionParams
}
Register-ScheduledTask @registerTaskParameters -Force
Get-ScheduledTask | Write-Verbose
}
End
{
Write-Information 'Registered task:'
Get-ScheduledTask -TaskName 'Run * by PowerShell' | Write-Information
}
}
Use of these functions:
$trigger = New-ScheduledTaskTrigger -At ((Get-Date).AddSeconds(10))
Register-CustomScheduledTask -Path "C:\temp\test.ps1" -Trigger $trigger
来源:https://stackoverflow.com/questions/61673254/scheduling-a-powershell-script-to-run-every-20-minutes