本文介绍了如何在 ScriptBlock 中传递 $_ ($PSItem)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm basically building my own parallel foreach pipeline function, using runspaces.

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

My problem is: I call my function like this:

somePipeline | MyNewForeachFunction { scriptBlockHere } | pipelineGoesOn...

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

How can I pass the $_ parameter correctly into the ScriptBlock? It works when the ScriptBlock contains as first line

param($_)

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

But as you might have noticed, the powershell built-in ForEach-Object and Where-Object do not need such a parameter declaration in every ScriptBlock that is passed to them.

提前感谢您的回答fjf2002

Thanks for your answers in advancefjf2002

目标是:我想让MyNewForeachFunction 函数的用户感到舒适——他们不需要在他们的脚本块中写一行param($_).

The goal is: I want comfort for the users of function MyNewForeachFunction - they shoudln't need to write a line param($_) in their script blocks.

MyNewForeachFunction中,ScriptBlock当前通过

Inside MyNewForeachFunction, The ScriptBlock is currently called via

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

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

The point is, how does for example the implementation of the built-in function ForEach-Object achieve that $_ need't be declared as a parameter in its ScriptBlock parameter, and can I use that functionality, too?

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

(If the answer is, ForEach-Object is a built-in function and uses some magic I can't use, then this would disqualify the language PowerShell as a whole in my opinion)

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

Thanks to mklement0, I could finally build my general foreach loop. Here's the code:

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,为什么 PowerShell 工作流比用于 XML 文件分析的非工作流脚本慢很多

Code partly from MathiasR.Jessen, Why PowerShell workflow is significantly slower than non-workflow script for XML file analysis

推荐答案

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

The key is to define $_ as a variable that your script block can see, via a call to Set-Variable.

这是一个简单的例子:

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]

这篇关于如何在 ScriptBlock 中传递 $_ ($PSItem)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-23 03:21