代码之家  ›  专栏  ›  技术社区  ›  Dan Finucane

是否可以从筛选器中终止或停止PowerShell管道

  •  9
  • Dan Finucane  · 技术社区  · 16 年前

    8 回复  |  直到 16 年前
        1
  •  8
  •   Maximum Cookie    10 年前

    可以使用任何可能中断外部循环或完全停止脚本执行(例如引发异常)的内容来中断管道。然后,解决方案是将管道包裹在一个循环中,如果需要停止管道,可以中断该循环。例如,下面的代码将从管道返回第一个项,然后通过断开外部do while循环来断开管道:

    do {
        Get-ChildItem|% { $_;break }
    } while ($false)
    

    此功能可以包装成这样的函数,其中最后一行完成与上面相同的任务:

    function Breakable-Pipeline([ScriptBlock]$ScriptBlock) {
        do {
            . $ScriptBlock
        } while ($false)
    }
    
    Breakable-Pipeline { Get-ChildItem|% { $_;break } }
    
        2
  •  3
  •   Steven Murawski    16 年前

    无法从下游命令停止上游命令。。它将继续筛选出与条件不匹配的对象,但第一个命令将处理它设置为处理的所有内容。

    除非您知道要从何处提取多少行,否则将读取整个文件以检查模式。

        3
  •  3
  •   Monsignor    5 年前

    gc demo.txt -ReadCount 1 | %{$num=0}{$num++; if($num -eq 5){throw "terminated pipeline!"}else{write-host $_}}
    

    看看这篇关于如何终止管道的文章: https://web.archive.org/web/20160829015320/http://powershell.com/cs/blogs/tobias/archive/2010/01/01/cancelling-a-pipeline.aspx

        4
  •  2
  •   EBGreen    16 年前

    Log Parser 查看是否不能在数据到达管道之前使用查询筛选数据。

        5
  •  1
  •   _guest_ _guest_    16 年前

    尝试使用这些过滤器,它们将强制管道在第一个对象(或前n个元素)之后停止,并将其存储在变量中;如果不将对象推出,但无法分配给变量,则需要传递变量的名称。

    filter FirstObject ([string]$vName = '') {
     if ($vName) {sv $vName $_ -s 1} else {$_}
     break
    }
    
    filter FirstElements ([int]$max = 2, [string]$vName = '') {
     if ($max -le 0) {break} else {$_arr += ,$_}
     if (!--$max) {
      if ($vName) {sv $vName $_arr -s 1} else {$_arr}
      break
     }
    }
    
    # can't assign to a variable directly
    $myLog = get-eventLog security | ... | firstObject
    
    # pass the the varName
    get-eventLog security | ... | firstObject myLog
    $myLog
    
    # can't assign to a variable directly
    $myLogs = get-eventLog security | ... | firstElements 3
    
    # pass the number of elements and the varName
    get-eventLog security | ... | firstElements 3 myLogs
    $myLogs
    
    ####################################
    
    get-eventLog security | % {
     if ($_.timegenerated -lt (date 11.09.08) -and`
      $_.timegenerated -gt (date 11.01.08)) {$log1 = $_; break}
    }
    
    #
    $log1
    
        6
  •  1
  •   Χpẘ    9 年前

    如果你愿意使用非公开的成员,这里是一个阻止管道的方法。它模仿什么 select-object 做。 invoke-method (化名 im )是调用非公共方法的函数。 select-property (化名 selp )是一个选择(类似于选择对象)非公共属性的函数,但是它自动地像 -ExpandProperty 如果只找到一个匹配的属性。(我写道 选择属性 调用方法 在工作中,所以不能共享这些的源代码)。

    # Get the system.management.automation assembly
    $script:smaa=[appdomain]::currentdomain.getassemblies()|
             ? location -like "*system.management.automation*"
    # Get the StopUpstreamCommandsException class 
    $script:upcet=$smaa.gettypes()| ? name -like "*StopUpstreamCommandsException *"
    
    function stop-pipeline {
        # Create a StopUpstreamCommandsException
        $upce = [activator]::CreateInstance($upcet,@($pscmdlet))
    
        $PipelineProcessor=$pscmdlet.CommandRuntime|select-property PipelineProcessor
        $commands = $PipelineProcessor|select-property commands
        $commandProcessor= $commands[0]
    
        $ci = $commandProcessor|select-property commandinfo
        $upce.RequestingCommandProcessor | im set_commandinfo @($ci)
    
        $cr = $commandProcessor|select-property commandruntime
        $upce.RequestingCommandProcessor| im set_commandruntime @($cr)
    
        $null = $PipelineProcessor|
            invoke-method recordfailure @($upce, $commandProcessor.command)
    
        if ($commands.count -gt 1) {
          $doCompletes = @()
          1..($commands.count-1) | % {
            write-debug "Stop-pipeline: added DoComplete for $($commands[$_])"
            $doCompletes += $commands[$_] | invoke-method DoComplete -returnClosure
          }
          foreach ($DoComplete in $doCompletes) {
            $null = & $DoComplete
          }
        }
    
        throw $upce
    }
    

    编辑:根据mklement0的注释:

    这是一个 link 到Nivot ink博客的“poke”模块的脚本,该模块同样允许访问非公共成员。

    选择对象 显示。原始的MS注释(如果有的话)当然不在反编译中。坦率地说,我不知道函数使用的各种类型的目的。要达到这样的理解水平,可能需要付出相当大的努力。

    我的建议是:去拿奥辛的戳模。调整代码以使用该模块。然后试试看。如果你喜欢它的工作方式,那就用它,不要担心它的工作方式(我就是这么做的)。

    注:我没有深入研究过“戳”,但我猜它没有 -returnClosure . 不过,补充这一点应该很容易:

    if (-not $returnClosure) {
      $methodInfo.Invoke($arguments)
    } else {
      {$methodInfo.Invoke($arguments)}.GetNewClosure()
    }
    
        7
  •  0
  •   Emperor XLII    13 年前

    另一种选择是使用 -file a上的参数 switch 陈述。使用 -文件 将一次读取一行文件,您可以使用 break 立即退出而不读取文件的其余部分。

    switch -file $someFile {
      # Parse current line for later matches.
      { $script:line = [DateTime]$_ } { }
      # If less than min date, keep looking.
      { $line -lt $minDate } { Write-Host "skipping: $line"; continue }
      # If greater than max date, stop checking.
      { $line -gt $maxDate } { Write-Host "stopping: $line"; break }
      # Otherwise, date is between min and max.
      default { Write-Host "match: $line" }
    }
    
        8
  •  0
  •   mklement0    7 年前

    这里有一个不完美的实现 Stop-Pipeline cmdlet(需要PS v3+),感谢您改编自 this answer :

    #requires -version 3
    Filter Stop-Pipeline {
      $sp = { Select-Object -First 1 }.GetSteppablePipeline($MyInvocation.CommandOrigin)
      $sp.Begin($true)
      $sp.Process(0)
    }
    
    # Example
    1..5 | % { if ($_ -gt 2) { Stop-Pipeline }; $_ } # -> 1, 2
    

    :我不完全理解它是如何工作的,尽管它从根本上利用了 Select -First 过早停止管道的能力(PS v3+)。然而,在这种情况下 选择-第一个 终止管道: 下游 Cmdlet(命令 在管道中)没有机会运行 end 阻碍。
    聚合Cmdlet (必须接受的 全部的 在产生输出之前的输入,例如 Sort-Object , Group-Object ,和 Measure-Object ) 如果稍后放置在同一管道中,则不会生成输出 ;例如:

    # !! NO output, because Sort-Object never finishes.
    1..5 | % { if ($_ -gt 2) { Stop-Pipeline }; $_ } | Sort-Object
    

    可能导致更好解决方案的背景信息:

    多亏了 PetSerAl answer here 演示如何生成相同的异常 Select-Object -First 用于内部停止上游cmdlet。

    但是,会从cmdlet内部引发异常,即 自身连接到管道停止

    停止管道 连接到应停止的管道(仅封闭 ForEach-Object ( % )问题是:如何在目标管道的上下文中引发异常?