我正在使用Invoke-Command
,并且在-ScriptBlock
内我正在使用Start-Job
。我必须在Start-Job
中使用$Using:var
,但会话正在局部会话中查找声明的变量(在Invoke-Command
之前声明(。以下是我正在做的事情的一个非常简短的示例:
Invoke-Command -ComputerName $computer -ScriptBlock {
$sourcePath = 'C:Source'
$destPath = 'C:dest.zip'
$compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal
$includeBaseDirectory = $false
Start-Job -Name "compress_archive" -ScriptBlock {
Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::CreateFromDirectory("$using:sourcePath","$using:destPathTemp",$using:compressionLevel,$using:includeBaseDirectory)
}
}
Invoke-Command : The value of the using variable '$using:sourcePath' cannot be retrieved because it has not been set in the local session.
At line:1 char:1
+ Invoke-Command -ComputerName vode-fbtest -ScriptBlock {
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Invoke-Command], RuntimeException
+ FullyQualifiedErrorId : UsingVariableIsUndefined,Microsoft.PowerShell.Commands.InvokeCommandCommand
如果我在Start-Job -ScriptBlock {}
中调用变量时省略$using
,则会出现Cannot find an overload for "CreateFromDirectory" and the argument count: "4".
错误,因为变量未在该范围内定义。
有没有办法将$using
用于远程会话中的变量而不是本地变量,或者我可以指定的另一个范围,该范围将从远程会话中获取变量?我可以在Invoke-Command
之前在本地声明这些变量来解决此问题,但由于包含动态值的变量,这将需要大量的工作(所有这些都在一个foreach ($obj in $objects)
中,其数据是在远程计算机上检索的,因此如果我无法完成这项工作,我需要重组整个脚本(。
我正在Windows Server 2012 R2(源主机和调用命令的-ComputerName
主机(上使用PS v5.1,如果这有什么不同的话。
查看此答案,我发现您可以将变量公开给较低级别的脚本块,但我需要从远程会话中实际声明变量。该值需要来自运行远程会话的计算机。是否可以从远程会话中声明变量,使其可用于顶级脚本块中的脚本块?
PetSerAl,和以前无数次一样,在关于这个问题的简短评论中提供了关键的指示:
您需要:
-
使用
[scriptblock]::Create()
创建脚本块,以便从字符串动态传递给Start-Job
-
在
Invoke-Command
脚本块中进行[scriptblock]::Create()
调用,因为只有这样才能确保其中声明的变量是[scriptblock]::Create()
创建的脚本块中通过$using:
作用域说明符引用的变量。- 相比之下,如果您使用脚本块文本,
{ ... }
与Start-Job
,就像在您的尝试中一样,$using:
引用不是指Invoke-Command
脚本块的作用域,而是指向Invoke-Command
调用者的作用域,即对进行整体Invoke-Command
调用的代码可见的变量。 - 理想情况下,
$using:...
引用的扩展将足够智能,可以处理嵌套范围,如本例所示,但PowerShell Core 7.0.0-preview.3并非如此。
- 相比之下,如果您使用脚本块文本,
警告:正如 PetSerAl 指出的那样,如果您将Invoke-Command
与命令范围的临时会话一起使用(通过使用-ComputerName
暗示(,而不是在New-PSSession
之前创建并传递给Invoke-Command
的较长生存期会话-Session
- 当Invoke-Command
调用返回时,后台作业(可能(有机会完成之前就会终止。虽然你可以通过Start-Job
电话给... | Receive-Job -Wait -AutoRemove
,但只有当你开始多个工作时,这才是值得的。
因此:
Invoke-Command -ComputerName $computer -ScriptBlock {
# Inside this remotely executing script block, define the variables
# that the script block passed to Start-Job below will reference:
$sourcePath = 'C:Source'
$destPath = 'C:dest.zip'
$compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal
$includeBaseDirectory = $false
# Define the Start-Job script block as *literal* (here-)*string*, so as
# to defer interpretation of the $using: references, and then
# construct a script block from it using [scriptblock]::Create(), which
# ties the $using: references to *this* scope.
$jobSb = [scriptblock]::Create(
@'
Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::CreateFromDirectory("$using:sourcePath","$using:destPathTemp",$using:compressionLevel,$using:includeBaseDirectory)
'@
)
Start-Job -Name "compress_archive" -ScriptBlock $jobSb
}