powershell - 如何在 ScriptBlock 中传递 $_ ($PSItem)

标签 powershell foreach parallel-processing pipe powershell-sdk

我基本上是使用运行空间构建自己的并行 foreach 管道函数。

我的问题是:我这样调用我的函数:

somePipeline | MyNewForeachFunction { scriptBlockHere } | pipelineGoesOn...

如何将 $_ 参数正确传递到 ScriptBlock 中?当 ScriptBlock 包含作为第一行时它起作用

param($_)

但是您可能已经注意到,powershell 内置的 ForEach-Object 和Where-Object 不需要在传递给它们的每个 ScriptBlock 中都需要这样的参数声明。

感谢您提前的答复 fjf2002

编辑:

目标是:我希望函数 MyNewForeachFunction 的用户感到舒适 - 他们不需要在脚本 block 中编写一行 param($_)

MyNewForeachFunction内,ScriptBlock当前通过

调用
$PSInstance = [powershell]::Create().AddScript($ScriptBlock).AddParameter('_', $_)
$PSInstance.BeginInvoke()

编辑2:

问题在于,例如内置函数 ForEach-Object 的实现是如何实现 $_ 不需要在其函数中声明为参数的? ScriptBlock 参数,我也可以使用该功能吗?

(如果答案是,ForEach-Object 是一个内置函数,并且使用了一些我无法使用的魔法,那么在我看来,这将取消 PowerShell 语言的整体资格)

编辑3:

感谢 mklement0,我终于可以构建我的通用 foreach 循环。代码如下:

function ForEachParallel {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory)] [ScriptBlock] $ScriptBlock,
        [Parameter(Mandatory=$false)] [int] $PoolSize = 20,
        [Parameter(ValueFromPipeline)] $PipelineObject
    )

    Begin {
        $RunspacePool = [runspacefactory]::CreateRunspacePool(1, $poolSize)
        $RunspacePool.Open()
        $Runspaces = @()
    }

    Process {
        $PSInstance = [powershell]::Create().
            AddCommand('Set-Variable').AddParameter('Name', '_').AddParameter('Value', $PipelineObject).
            AddCommand('Set-Variable').AddParameter('Name', 'ErrorActionPreference').AddParameter('Value', 'Stop').
            AddScript($ScriptBlock)

        $PSInstance.RunspacePool = $RunspacePool

        $Runspaces += New-Object PSObject -Property @{
            Instance = $PSInstance
            IAResult = $PSInstance.BeginInvoke()
            Argument = $PipelineObject
        }
    }

    End {
        while($True) {
            $completedRunspaces = @($Runspaces | where {$_.IAResult.IsCompleted})

            $completedRunspaces | foreach {
                Write-Output $_.Instance.EndInvoke($_.IAResult)
                $_.Instance.Dispose()
            }

            if($completedRunspaces.Count -eq $Runspaces.Count) {
                break
            }

            $Runspaces = @($Runspaces | where { $completedRunspaces -notcontains $_ })
            Start-Sleep -Milliseconds 250
        }

        $RunspacePool.Close()
        $RunspacePool.Dispose()
    }
}

部分代码来自MathiasR.Jessen,Why PowerShell workflow is significantly slower than non-workflow script for XML file analysis

最佳答案

关键是通过调用 Set-Variable$_ 定义为脚本 block 可以看到的变量

这是一个简单的例子:

function MyNewForeachFunction {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory)]
    [scriptblock] $ScriptBlock
    ,
    [Parameter(ValueFromPipeline)]
    $InputObject
  )

  process {
    $PSInstance = [powershell]::Create()

    # Add a call to define $_ based on the current pipeline input object
    $null = $PSInstance.
      AddCommand('Set-Variable').
        AddParameter('Name', '_').
        AddParameter('Value', $InputObject).
      AddScript($ScriptBlock)

    $PSInstance.Invoke()
  }

}

# Invoke with sample values.
1, (Get-Date) | MyNewForeachFunction { "[$_]" }

上面的结果类似于:

[1]
[10/26/2018 00:17:37]

关于powershell - 如何在 ScriptBlock 中传递 $_ ($PSItem),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52975186/

相关文章:

r - 无法连接 data.table 结果来自 R 中的 foreach 循环

c - 具有加速功能的示例 openmp 程序

java - 为什么我的多线程排序算法不比我的单线程归并排序快

javascript - 从 vue.js 中的 foreach 循环访问变量

powershell/sqlplus 错误 SP2-0042 : unknown command " ■@" - rest of line ignored

c# - 如何在带有配置文件的 Powershell 脚本中使用自定义 WCF 代理?

Powershell ISE缓存变量值?

java - 数组列表+数据库+servlet+DAO

r - AWS EC2 上 doredis 中的套接字连接错误

json - "The property cannot be found on this object. Verify that the property exists."属性肯定存在