$Using:var 在 Invoke-Command 内的 Start-Job 中

Dus*_*gas 4 powershell invoke-command powershell-jobs

我正在使用Invoke-Command,并且在-ScriptBlock我正在使用的范围内Start-Job。我必须使用$Using:varinside Start-Job,但会话正在本地会话中查找声明的变量(之前声明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
Run Code Online (Sandbox Code Playgroud)

如果我$using在调用变量时省略,Start-Job -ScriptBlock {}那么我会收到Cannot find an overload for "CreateFromDirectory" and the argument count: "4".错误,因为变量未在该范围内定义。

有没有一种方法可以在远程会话中而不是本地会话中使用$using变量,或者我可以指定另一个范围来从远程会话中获取变量?我可以在修复此问题之前在本地声明这些变量,但这需要大量工作,因为变量包含动态值(所有这些都在 a 中,其数据是在远程计算机上检索的,因此我需要如果我无法完成这项工作,请重新构建整个脚本)。Invoke-Commandforeach ($obj in $objects)

-ComputerName我在 Windows Server 2012 R2(源主机和调用命令的主机)上使用 PS v5.1(如果这有什么区别的话)。

看看这个答案,我发现您可以将变量公开给较低级别​​的脚本块,但我需要实际从远程会话中声明变量。该值需要来自运行远程会话的计算机。您能否以某种方式从远程会话中声明该变量,使其可用于顶级脚本块中的脚本块?

mkl*_*nt0 5

正如之前无数次一样, PetSerAl在对该问题的简短评论中提供了关键的指针:

你需要:

  • 用于创建要从字符串动态[scriptblock]::Create()传递的脚本块Start-Job

  • 在脚本块进行调用[scriptblock]::Create(),因为只有这样才能确保其中声明的变量 是通过范围说明符在创建的脚本块中引用的变量。Invoke-Command[scriptblock]::Create()$using:

    • 相比之下,如果您使用脚本块文字,{ ... }Start-Job,如您的尝试,$using:引用不是Invoke-Command脚本块的范围,而是指的调用者的范围Invoke-Command,即对使整体Invoke-Command调用。
    • 理想情况下,引用的扩展$using:...足够智能,可以处理嵌套范围(如本例所示),但 PowerShell Core 7.0.0-preview.3 的情况并非如此。

警告:正如 PetSerAl 指出的那样,如果您使用Invoke-Command命令范围的临时会话(通过使用暗示) - 而不是在with之前创建并传递给with-ComputerName的长期会话-当调用返回时,后台作业将终止,在它(可能)有机会完成之前。虽然您可以通过管道将调用传递给,但只有当您启动多个作业时,这才值得。New-PSSessionInvoke-Command-SessionInvoke-CommandStart-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

}
Run Code Online (Sandbox Code Playgroud)