问题
I need to schedule a PowerShell task like following:
powershell.exe -file ..\Execute\execute.ps1
And I have tried assigning it to an argument $Argument
then pass it to schtasks
like following:
$Argument = "powershell.exe -file '..\Execute\execute.ps1'"
schtasks /create /tn "SOE_Checks" /tr $Argument /sc DAILY /st 05:00 /ru "System" /rl HIGHEST /f
but after running above code, nothing happened - while the task is created successfully, it appears not to run.
I have also tried assigning it to $Argument
without the quotes, it worked but I got the following warnings:
ERROR: Invalid syntax. Value expected for '/tr'.
Type "SCHTASKS /CREATE /?" for usage.
Can anyone please let me know what I have done wrong here? (I am aware that I can accomplish this using PowerShell's New-ScheduledTaskAction
but I want it to work this way)
Just want to add that if I change the file path to a specific location in $Argument
like this, $Argument = "powershell.exe -file 'C:\SOE\Execute\execute.ps1'"
, it works fine without any warnings but this is not ideal.
I have read this but it does not work for me
回答1:
Scheduled tasks created with schtasks.exe
execute with the working directory set to $env:windir\system32
[1], so unless your script happens to be located in ..\Execute\execute.ps1
relative to there, your command won't work as intended.
If you don't want to hard-code the script path directly into the command, construct the command dynamically, by resolving the relative path to an absolute one when you assign to $Argument
:
$Argument = 'powershell.exe -file \"{0}\"' -f (Convert-Path ..\Execute\execute.ps1)
Note the - unfortunate - need to escape the embedded "
as \"
, which is longstanding bug that hasn't been fixed for the sake of backward compatibility - see this GitHub docs issue for background.
Convert-Path
resolves a relative path to an absolute one.
Note that the relative path must refer to an existing file (or directory).
Similarly, relative paths inside your script will be relative to $env:windir\system32
too; to make them relative to the script's directory, change to your script's directory first by executing Set-Location $PSScriptRoot
at the start of your script.
Optional reading: How to quote commands that run from a scheduled task:
Note: Virtually the same rules apply as when running a command from the Windows Run dialog (press WinKey+R), which you can use to test-drive a command (the command to pass to schtasks /tr
, without outer quoting, not the whole schtasks
command line) - though note that the working directory will be the user's home directory, and that you won't be able to use '...'
-quoting around the PowerShell CLI's -File
argument - see below):
cmd.exe
is NOT involved during execution, which means:You needn't worry about non-double-quoted use of
cmd.exe
metacharacters such as&
, for instance, so you can use these characters even in single-quoted strings passed to the PowerShell CLIpowershell.exe
as (part of) the-Command
argument(s).Conversely, output redirections (e.g.,
> c:\path\to\log.txt
) are not directly supported.In the context of invoking the PowerShell CLI, this means:
With
-File
, you cannot use them on the command line and must instead perform them from within your script.With
-Command
, however, you can use them, because it is then PowerShell that applies them (but note that Windows PowerShell's>
operator creates UTF-16LE files).
(Even though
cmd.exe
isn't involved) references to environment variables using the same syntax form as incmd.exe
are expanded (e.g.,%USERNAME%
)- Caveat: You cannot escape such references:
%%
doesn't work - the additional%
is simply treated as a literal, and expansion still occurs; e.g.,%%OS%%
results in%Windows_NT%
.^%
(accidentally) prevents expansion, but retains the^
- the^
doesn't escape; rather, it "disrupts" the variable name, in which case the token is left as-is; e.g.,^%OS^%
results in^%OS^%
, i.e., is retained as-is.
The above applies to the commands as they must end up defined inside a scheduled task, as you would see or define them interactively in Task Scheduler (taskschd.msc
).
Additionally, for creating a scheduled task from the command line / a PowerShell script / a batch file:
you must quote the command as a whole and
comply with the syntax rules of the calling shell regarding escaping and up-front string interpolation.
(You can only get away without quoting if the command consists of only a single word that needs no escaping, such the path to an executable that contains no spaces or special characters and to which no arguments are passed.)
When calling schtasks.exe
[2], quote the /tr
argument as a whole as follows:
from PowerShell, use
"..."
, if you need to expand (string-interpolate) the command string up front; otherwise, use'...'
.
Important: The need to escape nested"
as\"
applies in both cases, which in the case of outer"..."
quoting means that nested"
must be escaped as\`"
(sic).- Surprisingly,
schtasks.exe
recognizes embedded'...'
quoting and automatically translates it to"..."
quoting - that is why your original command,"powershell.exe -file '..\Execute\execute.ps1'"
, worked, even though in direct invocation the PowerShell CLI does not support the use of'...'
in combination with-File
.
- Surprisingly,
from
cmd.exe
(whether directly or from a batch file), you must use"..."
.
PowerShell examples:
The following PowerShell commands create and execute two run-once scheduled tasks, named
test1
andtest2
, that run when the next calendar minute starts, in the context of the calling user, visibly. (You'll have to remove these tasks manually afterwards.)You may have to wait for up to 1 minute to see the invocation kick in, at which point a new console window pops up for each task.
# Create sample script test.ps1 in the current dir. that
# echoes its arguments and then waits for a keypress.
'"Hi, $Args."; Read-Host "Press ENTER to exit"' > test.ps1
# Find the start of the next calendar minute.
$nextFullMinute = ([datetime]::Now.AddMinutes(1).TimeOfDay.ToString('hh\:mm'))
# -File example:
# Invoke test.ps1 and pass it 'foo' as an argument.
# Note the escaped embedded "..." quoting around the script path
# and that with -File you can only pass literal arguments at
# invocation time).
schtasks.exe /create /f /tn test1 /sc once /st $nextFullMinute `
/tr "powershell -File \`"$PWD/test.ps1\`" foo" #`# (dummy comment to fix broken syntax highlighting)
# -Command example:
# Invoke test.ps1 and pass it $env:USERNAME as an argument.
# Note the '...' around the script path and the need to invoke it with
# &, as well as the ` before $env:USERNAME to prevent its premature expansion.
schtasks.exe /create /f /tn test2 /sc once /st $nextFullMinute `
/tr "powershell -Command & '$PWD/test.ps1' `$env:USERNAME"
"Tasks will execute at ${nextFullMinute}:00"
[1] Note that the Task Scheduler GUI allows you to configure a working directory, but this feature isn't available via the schtasks.exe
utility.
[2] The same applies to values passed to the -Argument
parameter of the New-ScheduledTaskAction
PowerShell cmdlet, though note that the executable name/path is specified separately there, via the -Execute
parameter.
By contrast, the Register-ScheduledJob
cmdlet for creating scheduled PowerShell jobs accepts a script block as the command to run, which eliminates the quoting headaches.
来源:https://stackoverflow.com/questions/55214284/how-to-escape-schtasks-tr-arguments