如何转义 schtasks /tr 参数

Posted

技术标签:

【中文标题】如何转义 schtasks /tr 参数【英文标题】:How to escape schtasks /tr arguments 【发布时间】:2019-08-08 09:40:56 【问题描述】:

我需要安排一个 PowerShell 任务,如下所示:

powershell.exe -file ..\Execute\execute.ps1

我已经尝试将其分配给参数$Argument,然后将其传递给schtasks,如下所示:

$Argument = "powershell.exe -file '..\Execute\execute.ps1'"
schtasks /create /tn "SOE_Checks" /tr $Argument /sc DAILY /st 05:00 /ru "System" /rl HIGHEST /f

但是在运行上面的代码之后,什么都没有发生——虽然任务创建成功,但它似乎没有运行。

我也尝试将其分配给 $Argument 不带引号,它有效,但我收到以下警告:

ERROR: Invalid syntax. Value expected for '/tr'.
Type "SCHTASKS /CREATE /?" for usage.

谁能告诉我我在这里做错了什么? (我知道我可以使用 PowerShell 的 New-ScheduledTaskAction 完成此操作,但我希望它以这种方式工作)

只想补充一点,如果我像这样将文件路径更改为$Argument 中的特定位置,$Argument = "powershell.exe -file 'C:\SOE\Execute\execute.ps1'",它可以正常工作而没有任何警告,但这并不理想。

我已阅读 this 但它对我不起作用

【问题讨论】:

【参考方案1】:

使用schtasks.exe 创建的计划任务在工作目录设置为$env:windir\system32[1] 的情况下执行,因此除非您的脚本恰好位于..\Execute\execute.ps1相对于那里,您的命令将无法按预期工作。

如果您不想将脚本路径直接硬编码到命令中,动态构造命令,将相对路径解析为绝对 分配给$Argument时的一个

$Argument = 'powershell.exe -file \"0\"' -f (Convert-Path ..\Execute\execute.ps1)

注意 - 不幸的是 - 需要将嵌入的 " 转义为 \",这是一个长期存在的错误,为了向后兼容而尚未修复 - 有关背景,请参阅 this GitHub docs issue。

Convert-Path 将相对路径解析为绝对路径。 请注意,相对路径必须引用现有文件(或目录)。

同样,相对路径内部你的脚本也将相对于$env:windir\system32;要使它们相对于脚本的目录,首先通过在脚本开始处执行Set-Location $PSScriptRoot 切换到脚本的目录。


可选阅读:如何引用从计划任务运行的命令:

注意:几乎相同的规则适用于从 Windows 运行对话框运行命令(按 WinKey+R),其中 您可以使用 test-drive 一个命令(传递给 schtasks /tr 的命令,没有外部引用,而不是整个 schtasks 命令行) - 但请注意工作目录将是用户的主目录,并且您将无法使用 '...'-引用 PowerShell CLI 的 -File 参数 - 见下文):

cmd.exe在执行期间不参与,这意味着:

您不必担心cmd.exe 元字符(例如&)的非双引号使用,因此您甚至可以在单引号中使用这些字符传递给 PowerShell CLI powershell.exe 的字符串作为 -Command 参数的(一部分)。

相反,不直接支持输出重定向(例如,> c:\path\to\log.txt)。

在调用 PowerShell CLI 的上下文中,这意味着:

使用-File,您不能在命令行上使用它们,而必须从您的脚本中执行它们。

但是,使用-Command,您可以使用它们,因为应用它们的是 PowerShell(但请注意,Windows PowerShell 的 > 运算符创建 UTF-16LE 文件)。

(尽管cmd.exe不涉及环境变量的引用使用与cmd.exe中相同的语法形式是 扩展(例如,%USERNAME%

警告:您无法逃避此类引用%% 不起作用 - 额外的 % 被简单地视为文字,并且仍然会发生扩展;例如,%%OS%% 结果为 %Windows_NT%^%(意外地)阻止了扩展,但保留了^——^ 没有转义;相反,它“破坏”了变量名,在这种情况下,令牌保持原样;例如,^%OS^% 产生 ^%OS^%,即按原样保留。

以上内容适用于命令,因为它们必须结束在计划任务中定义,正如您在任务计划程序 (taskschd.msc) 中以交互方式查看或定义它们一样。

另外,用于创建计划任务从命令行/PowerShell 脚本/批处理文件

你必须引用命令作为一个整体

遵守调用外壳程序关于转义和预先字符串插值的语法规则。

(如果命令只包含一个不需要转义的单个单词,例如不包含空格的可执行文件的路径或特殊字符,不传递任何参数。)

调用schtasks.exe[2]时,/tr参数作为一个整体引用如下

来自 PowerShell,使用 "...",如果您需要扩展(字符串插值)命令字符串在前面;否则,请使用'...'重要提示:需要将" 转义为\",这两种情况都适用,在外部@ 的情况下987654364@ 引用意味着嵌套的" 必须转义为\`"(原文如此)。

令人惊讶的是,schtasks.exe 识别 嵌入 '...' 引用并自动将其转换为 "..." 引用 - 这就是您原来的命令 "powershell.exe -file '..\Execute\execute.ps1'" 有效的原因,即使在 direct 调用中,PowerShell CLI 确实支持将 '...'-File 结合使用。

来自cmd.exe(无论是直接还是来自批处理文件),您必须使用"..."

PowerShell 示例

以下 PowerShell 命令创建并执行两个 run-once 计划任务,名为 test1test2,在下一个日历分钟开始时运行,在调用用户的上下文中,可见。 (之后您必须手动删除这些任务。)

您可能需要等待最多 1 分钟才能看到调用开始,此时将为每个任务弹出一个新的控制台窗口。

# 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] 请注意,任务计划程序 GUI 允许您配置工作目录,但此功能无法通过 schtasks.exe 实用程序使用。

[2] 这同样适用于传递给 New-ScheduledTaskAction PowerShell cmdlet 的 -Argument 参数的值,但请注意,可执行文件名称/路径在此处通过 -Execute 参数单独指定。 相比之下,用于创建计划 PowerShell 作业Register-ScheduledJob cmdlet 接受 脚本块 作为要运行的命令,这消除了引用问题。

【讨论】:

此外,为了解决他们的问题,他们需要将引号嵌入到他们的命令$arg = '"powershell -file '...'"'; ... /TR $arg @TheIncorrigible1:虽然这没有什么坏处,但也没有必要——即使是带有嵌入空格和 cmd.exe 元字符的文件名,例如 &(不涉及 cmd.exe在调用中)。请查看我的更新。 不幸的是,它对我不起作用,但如果我将代码更改为$Argument = powershell.exe -file "..\Execute\execute.ps1"(仅引用相对路径),那么它可以完美运行,但我不知道为什么。你能解释一下吗?无论如何我都会选择你的答案,因为我从中学到了很多东西,我也会发布一个对我有用的答案,以防万一:)谢谢 谢谢,@JiaHao - 但是你的命令立即执行你的脚本,这不是你想要的。至于这个答案中的解决方案:在赋值后检查$Argument的值,看看是否包含正确的脚本完整路径。

以上是关于如何转义 schtasks /tr 参数的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 SCHTASKS 在 SYSTEM 用户帐户下创建计划任务

如何在不打开新命令行窗口的情况下使用“schtasks”执行计划任务?

如何通过asp.net schtasks将参数传递给bat文件

如何使用 unix 实用程序 (sed/tr/awk) 用非转义等效项替换所有转义序列

schtasks命令

维持权限schtasks命令