【问题标题】:Calling PowerShell Start-Job cmdlet with multiple parameters and foreach loop is soooo close使用多个参数和 foreach 循环调用 PowerShell Start-Job cmdlet 非常接近
【发布时间】:2018-01-31 07:35:58
【问题描述】:

我有几千台计算机将安全事件日志备份到服务器的共享中。环境非常动态,因此需要自动化。

我一直在编写一个创建哈希的脚本,其中每个键是一个序列,每个键的值是 N 台计算机。我将键和值传递给另一个脚本,该脚本将运行 n 个作业来备份日志; n 取决于我可以在每个作业中包含多少台机器,并且仍然可以有效地处理备份。

脚本 1 有这个块:

foreach ($key in ($arrayAll.Keys | Sort-Object)) {
    Job-EvtLog.ps1 $key @($data)
}

脚本 2 有:

Param(
    [Parameter[Mandatory=$true, ValueFromPipeline=$true)]
    [string[]] $Key,

    [Parameter[Mandatory=$true, ValueFromPipeline=$true)]
    [Array[]] $Computers
)

function job_process($key) {
    #...stuff...including casting the @($Computers) to the array: $MyComputers
    $jobCommand = [ScriptBlock]::Create("foreach(`$d in $MyComputers) {Add-Content -Path $somewhere -Value `$d}")
    Start-Job -Name $key $jobCommand -Args $somewhere $MyComputers
}

我正在通过尝试将计算机数组写入文件来测试这一点,因此Add-Content

我显然在创建脚本块时做错了。 Get-Job | %{$_.Command} 显示:

foreach ($d in my_List_of_Hostnames) {Add-Content -Path myCorrectpath -Value $d}

没有向 myCorrectPath 写入任何内容。

如果我写:

... -Value `$d}")

在脚本块的末尾,显示屏会显示主机名列表中的最后一个主机名。

如何编写脚本块,使其遍历脚本块中的主机名数组以处理一个作业中的每个元素?

【问题讨论】:

    标签: powershell start-job scriptblock


    【解决方案1】:

    在某些情况下,从字符串创建脚本块是有意义的。你的不是其中之一。

    在您的代码中,字符串

    "foreach (`$d in $MyComputers) {Add-Content -Path $somewhere -Value `$d}"
    

    应该扩展为这样的语句(假设$MyComputers$somewhere 的任意样本值):

    foreach ($d in A B C) {Add-Content -Path C:\some\folder -Value $d}
    

    但是,A B C 不是有效列表,这意味着 PowerShell 会尝试将 A 作为命令调用,因此您的循环应该会产生如下错误:

    A :术语“A”未被识别为 cmdlet、函数、脚本文件或可运行程序的名称。检查名称的拼写,如果包含路径,请验证路径是否正确,然后重试。

    您是否通过Receive-Job 收集作业输出进行验证?

    像这样创建和调用脚本块:

    $jobCommand = {
        Param($path, $computers)
        foreach ($d in $computers) {
            Add-Content -Path $path -Value $d
        }
    }
    Start-Job -Name $key -ScriptBlock $jobCommand -Args $somewhere, $MyComputers
    

    代码应该做你想做的事。

    确保$somewhere$MyComputers 实际上具有正确的值。

    【讨论】:

    • 谢谢;这可以按要求工作并解决我的问题。
    【解决方案2】:

    好的,让我们从脚本 2 的顶部开始:参数

    这是字符串的类型转换:[string]
    这是字符串数组的类型转换:[string[]]

    您是否希望$key 是一个字符串数组,或者只是一个字符串,因为您只传递一个字符串给它。相同的概念适用于 $Computers 期望一个数组。

    此外,您有两件事情从管道中接受了它们的价值,这只会使事情变得混乱。相反,也许您应该将其省略,或者将其更改为 ValueFromPipelineByPropertyName,如果您要将内容通过管道传输到其他内容,这是一个很棒的选择。

    接下来,您有一个带有 1 个参数的函数。在该函数中,您使用了几个变量,并以困难的方式制作了一个脚本块,这似乎并不明智。我认为可能更好的方法是:

    Param(
    [Parameter(Mandatory)]
    [string] $Key,
    [Parameter(Mandatory)]
    [string[]] $Computers)
    
    #...stuff...including casting the @($Computers) to the array: $MyComputers
    $jobCommand = {
        Param($JobPath,$JobComputers)
        foreach($d in $JobComputers) {add-content -Path $JobPath -Value $d}
    }
    start-job -name $key -scriptblock $jobCommand -argumentlist $somewhere $MyComputers
    

    那么你可以这样称呼它:

    foreach ($key in ($arrayAll.Keys | Sort-Object)) {
        Job-EvtLog.ps1 -Key $key -Computers $arrayAll[$key]
    }
    

    【讨论】:

    • 在我原来的帖子中,我留下了一行。我的电话有这行:$data = $arrayAll.item($key)。我认为它的净效果与您的建议相似,尽管不是那么干净。我按照您的指示更改了参数块。谢谢。
    猜你喜欢
    • 2018-03-24
    • 2016-12-18
    • 1970-01-01
    • 2019-10-20
    • 1970-01-01
    • 2021-12-13
    • 2015-05-14
    • 2018-01-02
    • 2021-08-22
    相关资源
    最近更新 更多