如何转义 schtasks /tr 参数
How to escape schtasks /tr arguments
我需要安排一个 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 = "powershell.exe -file 'C:\SOE\Execute\execute.ps1'"
将文件路径更改为 $Argument
中的特定位置,它可以正常工作,没有任何警告,但这不是理想。
我读过 this 但它对我不起作用
使用 schtasks.exe
创建的计划任务在 工作目录设置为 $env:windir\system32
[1] 时执行,因此,除非您的脚本恰好位于 ..\Execute\execute.ps1
相对于 那里 ,否则您的命令将无法按预期工作。
如果您不想将脚本路径直接硬编码到命令中,动态构建命令,方法是将相对路径解析为absolute 当你分配给 $Argument
:
一个
$Argument = 'powershell.exe -file \"{0}\"' -f (Convert-Path ..\Execute\execute.ps1)
请注意 - 不幸的是 - 需要将嵌入的 "
转义为 \"
,这是一个长期存在的错误,为了向后兼容尚未修复 - 请参阅this GitHub docs issue 为背景。
Convert-Path
将相对路径解析为绝对路径。
注意相对路径必须引用一个已经存在的文件(或目录)。
类似地,相对路径在你的脚本中也会相对于$env:windir\system32
;要使它们相对于脚本的目录,首先通过执行Set-Location $PSScriptRoot
[切换到你的脚本目录=213=] 在脚本的开头。
可选阅读:如何从计划任务中引用运行的命令:
注意:实际上 相同的规则 适用于 运行 从 Windows 运行 对话框中执行命令(按WinKey+R),你可以使用试驾命令(传递给 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
are expanded 相同的语法形式(例如,%USERNAME%
)
- 警告:您无法逃避此类引用:
%%
不起作用 - 额外的 %
被简单地视为文字,并且仍然会发生扩展;例如,%%OS%%
结果为 %Windows_NT%
。
^%
(意外地)阻止扩展,但 保留 ^
- ^
不会 escape ;相反,它 "disrupts" 变量名,在这种情况下,令牌保持原样;例如,^%OS^%
结果为 ^%OS^%
,即按原样保留。
以上适用于命令,因为它们必须结束在计划任务中定义,正如您在任务计划程序中看到或交互式定义它们一样(taskschd.msc
) .
另外,用于从命令行/PowerShell 脚本/批处理文件:
创建计划任务
你必须引用整个命令和
遵守调用 shell 关于转义和前期字符串插值的语法规则。
(如果命令只包含一个不需要转义的单个单词,你只能在没有引用的情况下离开,比如路径到不包含空格或特殊字符且未向其传递参数的可执行文件。)
调用schtasks.exe
[2]时,将/tr
参数整体引用如下:
来自 PowerShell,使用 "..."
,如果您需要预先扩展(字符串插入)命令字符串 ;否则,使用 '...'
.
重要:转义 nested "
as \"
的需要在这两种情况下都适用,在外部 "..."
引号意味着嵌套的 "
必须转义为 \`"
(原文如此)。
- 令人惊讶的是,
schtasks.exe
识别 embedded '...'
引用并自动将其转换为 "..."
引用 - 这就是为什么您的原始命令 "powershell.exe -file '..\Execute\execute.ps1'"
有效,即使在 direct 调用中 PowerShell CLI not 支持使用'...'
结合 -File
.
来自 cmd.exe
(无论是直接还是来自批处理文件),您 必须 使用 "..."
.
PowerShell 示例:
以下 PowerShell 命令创建并执行两个 运行-once
计划任务,名为 test1
和 test2
,即 运行 下一个日历分钟开始时,在调用用户的上下文中,可见。 (之后您必须手动删除这些任务。)
您可能需要等待最多 1 分钟才能看到调用启动,此时每个任务都会弹出一个新控制台 window。
# 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
参数的值,但请注意可执行文件 name/path 是单独指定的在那里,通过 -Execute
参数。
相比之下,用于创建计划的 PowerShell 作业 的 Register-ScheduledJob
cmdlet 接受 脚本块 作为 运行 的命令,它消除了引用的麻烦。
我需要安排一个 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 = "powershell.exe -file 'C:\SOE\Execute\execute.ps1'"
将文件路径更改为 $Argument
中的特定位置,它可以正常工作,没有任何警告,但这不是理想。
我读过 this 但它对我不起作用
使用 schtasks.exe
创建的计划任务在 工作目录设置为 $env:windir\system32
[1] 时执行,因此,除非您的脚本恰好位于 ..\Execute\execute.ps1
相对于 那里 ,否则您的命令将无法按预期工作。
如果您不想将脚本路径直接硬编码到命令中,动态构建命令,方法是将相对路径解析为absolute 当你分配给 $Argument
:
$Argument = 'powershell.exe -file \"{0}\"' -f (Convert-Path ..\Execute\execute.ps1)
请注意 - 不幸的是 - 需要将嵌入的 "
转义为 \"
,这是一个长期存在的错误,为了向后兼容尚未修复 - 请参阅this GitHub docs issue 为背景。
Convert-Path
将相对路径解析为绝对路径。
注意相对路径必须引用一个已经存在的文件(或目录)。
类似地,相对路径在你的脚本中也会相对于$env:windir\system32
;要使它们相对于脚本的目录,首先通过执行Set-Location $PSScriptRoot
[切换到你的脚本目录=213=] 在脚本的开头。
可选阅读:如何从计划任务中引用运行的命令:
注意:实际上 相同的规则 适用于 运行 从 Windows 运行 对话框中执行命令(按WinKey+R),你可以使用试驾命令(传递给 schtasks /tr
的命令,没有外引号,不是整个 schtasks
命令行)——但请注意,工作目录将是用户的主目录,而你不会能够在 PowerShell CLI 的 -File
参数周围使用 '...'
引号 - 见下文):
cmd.exe
在执行期间不涉及,这意味着:您不必担心
cmd.exe
元字符的非双引号使用,例如&
,因此您甚至可以在 单-引号字符串传递给 PowerShell CLIpowershell.exe
作为-Command
参数的(一部分)。相反,不直接支持输出重定向(例如,
> c:\path\to\log.txt
)。在调用 PowerShell CLI 的上下文中,这意味着:
使用
-File
,您不能在命令行上使用它们,而必须从 在 脚本中执行它们。使用
-Command
,但是,您 可以 使用它们,因为那时 PowerShell应用它们(但请注意 Windows PowerShell 的>
运算符创建 UTF-16LE 文件)。
(即使
cmd.exe
没有 涉及)对 环境变量的引用 使用与cmd.exe
are expanded 相同的语法形式(例如,%USERNAME%
)- 警告:您无法逃避此类引用:
%%
不起作用 - 额外的%
被简单地视为文字,并且仍然会发生扩展;例如,%%OS%%
结果为%Windows_NT%
。^%
(意外地)阻止扩展,但 保留^
-^
不会 escape ;相反,它 "disrupts" 变量名,在这种情况下,令牌保持原样;例如,^%OS^%
结果为^%OS^%
,即按原样保留。
以上适用于命令,因为它们必须结束在计划任务中定义,正如您在任务计划程序中看到或交互式定义它们一样(taskschd.msc
) .
另外,用于从命令行/PowerShell 脚本/批处理文件:
创建计划任务你必须引用整个命令和
遵守调用 shell 关于转义和前期字符串插值的语法规则。
(如果命令只包含一个不需要转义的单个单词,你只能在没有引用的情况下离开,比如路径到不包含空格或特殊字符且未向其传递参数的可执行文件。)
调用schtasks.exe
[2]时,将/tr
参数整体引用如下:
来自 PowerShell,使用
"..."
,如果您需要预先扩展(字符串插入)命令字符串 ;否则,使用'...'
.
重要:转义 nested"
as\"
的需要在这两种情况下都适用,在外部"..."
引号意味着嵌套的"
必须转义为\`"
(原文如此)。- 令人惊讶的是,
schtasks.exe
识别 embedded'...'
引用并自动将其转换为"..."
引用 - 这就是为什么您的原始命令"powershell.exe -file '..\Execute\execute.ps1'"
有效,即使在 direct 调用中 PowerShell CLI not 支持使用'...'
结合-File
.
- 令人惊讶的是,
来自
cmd.exe
(无论是直接还是来自批处理文件),您 必须 使用"..."
.
PowerShell 示例:
以下 PowerShell 命令创建并执行两个 运行-once 计划任务,名为
test1
和test2
,即 运行 下一个日历分钟开始时,在调用用户的上下文中,可见。 (之后您必须手动删除这些任务。)您可能需要等待最多 1 分钟才能看到调用启动,此时每个任务都会弹出一个新控制台 window。
# 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
参数的值,但请注意可执行文件 name/path 是单独指定的在那里,通过 -Execute
参数。
相比之下,用于创建计划的 PowerShell 作业 的 Register-ScheduledJob
cmdlet 接受 脚本块 作为 运行 的命令,它消除了引用的麻烦。