【问题标题】:Why does an argument passed into a script block fail to work?为什么传递给脚本块的参数不起作用?
【发布时间】:2018-10-05 18:52:15
【问题描述】:

完全披露:我的问题可能是基于对 Xen Desktop 的 Citrix PowerShell 模块的不完全理解。

我有以下脚本块。它在一个循环中被调用,对列表中的每个 VM 调用一次。我使用 PowerShell Jobs 是因为我想在作业运行时保持 UI 线程空闲以更新 UI。

代码“A”

$j = Start-Job -Name $jobName -ScriptBlock {
    param($url, $uuid, $cred, $snapshotName)
    $ErrorActionPreference = "Stop" 
    try
    {
        $error.clear()
        $xSS = $cred | Connect-XenServer -url $url -NoWarnCertificates -SetDefaultSession -PassThru; 
        $vm = (Get-XenVM -SessionOpaqueRef $xss.opaque_ref -uuid $uuid)
        #Create snapshot
        Invoke-XenVM -Async -SessionOpaqueRef $xss.opaque_ref -VM $vm -XenAction Snapshot -NewName $snapshotName
        return "OK"
    }
    catch
    {
        return ("ERROR: "+$error)
    }
} -ArgumentList $global:configFileVmMetaData.poolUrl, $xenVm.key, $global:cred, $snapshotName

代码“A”可以正常工作,但它需要的时间比必要的要长,因为我每次调用脚本块时都在执行Connect-XenServer cmdlet。

所以,我尝试在循环外调用Connect-XenServer 并传入会话变量,如下面的代码“B” 所示。结果是错误Could not find open sessions to any XenServers 被抛出到脚本块中。我假设 $xss 会话变量在传递到脚本块时会以某种方式受到影响。

知道为什么 $xss 会话变量会被改变吗?

代码“B”

$xSS = $cred | Connect-XenServer -url $global:configFileVmMetaData.poolUrl -NoWarnCertificates -SetDefaultSession -PassThru; 

loop
{

    $j = Start-Job -Name $jobName -ScriptBlock {
        param($xss, $uuid, $snapshotName)
        $ErrorActionPreference = "Stop" 
        try
        {
            $error.clear()
            $vm = (Get-XenVM -SessionOpaqueRef $xss.opaque_ref -uuid $uuid)
            #Create snapshot
            Invoke-XenVM -Async -SessionOpaqueRef $xss.opaque_ref -VM $vm -XenAction Snapshot -NewName $snapshotName
            return "OK"
        }
        catch
        {
            return ("ERROR: "+$error)
        }
    } -ArgumentList $xss, $xenVm.key, $snapshotName

}

Robert Cotterman 答案启发的其他示例

在所有情况下,我仍然收到 Could not find open sessions to any XenServers 错误

仅供参考 - 使用 PowerShell 5.1

使用$using 的示例。变量内容按预期传入和传出

cls

$aLocal = "AAA"
$bLocal = "BBB"

$j = Start-Job -Name "TestJob" -ScriptBlock {
    return ($using:aLocal + " *** " + $using:bLocal)
    }

while ($true)
{
    $g = get-job -name "TestJob"

    write-host ("get-job " + $g.Name + " is " + $g.State)

    if ($g.State -ne "Running")
    {
        break
    }

    start-sleep -Seconds 1
}

write-host ("receive-Job='" + (receive-Job -Name "TestJob") +"'")

$g = get-Job -Name "TestJob"
Write-Host ("get-Job "+$g.name + " " + $g.state + " " + $g.HasMoreData + " " + $g.id)

if($g)
{
    Remove-Job -Name "TestJob"
}

输出

get-job TestJob is Running
get-job TestJob is Completed
receive-Job='AAA *** BBB'
get-Job TestJob Completed False 45
Remove-Job

使用 args 的示例。变量内容按预期传入和传出

cls

$aLocal = "AAA"
$bLocal = "BBB"

$j = Start-Job -Name "TestJob" -ScriptBlock {
    return ($args[0] + " *** " + $args[1])
    } -ArgumentList ($aLocal, $bLocal)

while ($true)
{
    $g = get-job -name "TestJob"

    write-host ("get-job " + $g.Name + " is " + $g.State)

    if ($g.State -ne "Running")
    {
        break
    }

    start-sleep -Seconds 1
}

write-host ("receive-Job='" + (receive-Job -Name "TestJob") +"'")

$g = get-Job -Name "TestJob"
Write-Host ("get-Job "+$g.name + " " + $g.state + " " + $g.HasMoreData + " " + $g.id)

if($g)
{
    Remove-Job -Name "TestJob"
}

输出

get-job TestJob is Running
get-job TestJob is Completed
receive-Job='AAA *** BBB'
get-Job TestJob Completed False 49

使用命名参数的示例。变量内容按预期传入和传出

cls

$aLocal = "AAA"
$bLocal = "BBB"

$j = Start-Job -Name "TestJob" -ScriptBlock {
    param($a, $b)
    return ($a + " *** " + $b)
    } -ArgumentList ($aLocal, $bLocal)

while ($true)
{
    $g = get-job -name "TestJob"

    write-host ("get-job " + $g.Name + " is " + $g.State)

    if ($g.State -ne "Running")
    {
        break
    }

    start-sleep -Seconds 1
}

write-host ("receive-Job='" + (receive-Job -Name "TestJob") +"'")

$g = get-Job -Name "TestJob"
Write-Host ("get-Job "+$g.name + " " + $g.state + " " + $g.HasMoreData + " " + $g.id)

if($g)
{
    Remove-Job -Name "TestJob"
}

输出

get-job TestJob is Running
get-job TestJob is Completed
receive-Job='AAA *** BBB'
get-Job TestJob Completed False 55

【问题讨论】:

  • 我只是不认为它是这样工作的。为您的 PowerShell 会话创建与 Xen 服务器的连接,并在 $xss 变量中收集有关该连接的信息。每个作业都运行自己的 PowerShell 会话。所以只是将$xss 传递给作业是不一样的,它只是对连接细节的描述。换句话说,我可以写下关于我的汽车的所有细节并交给你,但你不能开车去上班。
  • 同意。我需要重新考虑这一点。请作为答案发布
  • powershell 有一个内置的作业等待。 get-job | wait-job

标签: powershell scope xen scriptblock


【解决方案1】:

我只是不认为它是这样工作的。为您的 PowerShell 会话创建与 Xen 服务器的连接,并在 $xss 变量中收集有关该连接的信息。每个作业都运行自己的 PowerShell 会话。所以只是将 $xss 传递给作业是不一样的,它只是对连接细节的描述。

【讨论】:

    【解决方案2】:

    作业和调用命令要求您指定您正在使用您的变量。只需从

    更改变量
    $variable
    

    $using:variable
    

    内部变量不需要这个。但是从父脚本调用变量可以。

    另外,由于您将 $xss 作为参数传递,因此您不会使用 $xss 而是使用

    $args[0]
    

    因为这是你的第一个论点。第二个是 $args[1] 等等...... 原因是因为整个 xss 变量被打印为参数,并且没有在作业中命名。它被命名为 $args 并在第一个插槽 (0) 中占有一席之地。

    我更喜欢 $using:variable 因为它可以减少混淆

    【讨论】:

    • 请参阅上面“受Robert Cotterman 启发的其他示例”下的我的其他编码示例(3 个示例:$using、args、命名参数)。在所有情况下,我仍然收到 Could not find open sessions to any XenServers 错误。我认为 TheMadTechnician 在这种情况下是正确的。
    • 好吧,我不知道 XenServers 是如何工作的,对此感到抱歉。我的建议非常有效。但不适用于您的特定需求。如果我有它,我会试着弄清楚。也许如果你有每个工作实际上分别获取 xss 信息?那么当它得到它时,它是它自己的 powershell 实例所独有的吗?
    • 不用担心 - 感谢您抽出宝贵的时间。您让我探索了将数据传递到脚本块的各种方法。另外,我用this S.O. question 从不同的角度记录了我的问题和解决方案
    • 您是否尝试过让每个作业都执行它自己的 XSS 凭据抓取?这对我来说听起来可行。
    • 是的,但是获得 XSS 会话需要 5-10 秒,所以如果我说要创建 20-30 个快照,它会减慢整个工作。我最终将整个循环移到了工作中,所以我只需要在每个工作中获取一次 XSS 会话。见 S.O.我在给你的评论中提到的问题
    猜你喜欢
    • 2022-11-25
    • 2015-09-09
    • 1970-01-01
    • 2012-03-27
    • 2022-10-12
    • 2021-05-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多