简体   繁体   English

是否可以从过滤器中终止或停止 PowerShell 管道

[英]Is it possible to terminate or stop a PowerShell pipeline from within a filter

I have written a simple PowerShell filter that pushes the current object down the pipeline if its date is between the specified begin and end date.我编写了一个简单的 PowerShell 过滤器,如果当前对象的日期在指定的开始日期和结束日期之间,它会将当前对象推送到管道中。 The objects coming down the pipeline are always in ascending date order so as soon as the date exceeds the specified end date I know my work is done and I would like to let tell the pipeline that the upstream commands can abandon their work so that the pipeline can finish its work.管道中的对象始终按日期升序排列,因此只要日期超过指定的结束日期,我就知道我的工作已完成,我想告诉管道上游命令可以放弃其工作,以便管道可以完成它的工作。 I am reading some very large log files and I will frequently want to examine just a portion of the log.我正在阅读一些非常大的日志文件,我经常只想检查日志的一部分。 I am pretty sure this is not possible but I wanted to ask to be sure.我很确定这是不可能的,但我想问一下。

It is possible to break a pipeline with anything that would otherwise break an outside loop or halt script execution altogether (like throwing an exception).可以使用任何会中断外部循环或完全停止脚本执行(如抛出异常)的任何内容来中断管道。 The solution then is to wrap the pipeline in a loop that you can break if you need to stop the pipeline.解决方案是将管道包装在一个循环中,如果您需要停止管道,您可以中断该循环。 For example, the below code will return the first item from the pipeline and then break the pipeline by breaking the outside do-while loop:例如,下面的代码将从管道中返回第一项,然后通过中断外部 do-while 循环来中断管道:

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

This functionality can be wrapped into a function like this, where the last line accomplishes the same thing as above:这个功能可以包装成这样的函数,其中最后一行完成与上面相同的事情:

function Breakable-Pipeline([ScriptBlock]$ScriptBlock) {
    do {
        . $ScriptBlock
    } while ($false)
}

Breakable-Pipeline { Get-ChildItem|% { $_;break } }

It is not possible to stop an upstream command from a downstream command.. it will continue to filter out objects that do not match your criteria, but the first command will process everything it was set to process.无法从下游命令停止上游命令。它会继续过滤掉不符合条件的对象,但第一个命令将处理它设置为处理的所有内容。

The workaround will be to do more filtering on the upstream cmdlet or function/filter.解决方法是对上游 cmdlet 或函数/过滤器进行更多过滤。 Working with log files makes it a bit more comoplicated, but perhaps using Select-String and a regular expression to filter out the undesired dates might work for you.使用日志文件使它变得更加复杂,但也许使用 Select-String 和正则表达式来过滤掉不需要的日期可能对您有用。

Unless you know how many lines you want to take and from where, the whole file will be read to check for the pattern.除非您知道要取多少行以及从哪里取,否则将读取整个文件以检查模式。

You can throw an exception when ending the pipeline.您可以在结束管道时抛出异常。

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

or或者

Look at this post about how to terminate a pipeline: https://web.archive.org/web/20160829015320/http://powershell.com/cs/blogs/tobias/archive/2010/01/01/cancelling-a-pipeline.aspx看看这篇关于如何终止管道的帖子: https : //web.archive.org/web/20160829015320/http : //powershell.com/cs/blogs/tobias/archive/2010/01/01/cancelling-a -pipeline.aspx

不确定您的确切需求,但可能值得您花时间查看Log Parser ,看看您是否无法在数据到达管道之前使用查询来过滤数据。

If you're willing to use non-public members here is a way to stop the pipeline.如果您愿意使用非公开成员,这里是一种停止管道的方法。 It mimics what select-object does.它模仿了select-object作用。 invoke-method (alias im ) is a function to invoke non-public methods. invoke-method (别名im )是一个调用非公共方法的函数。 select-property (alias selp ) is a function to select (similar to select-object) non-public properties - however it automatically acts like -ExpandProperty if only one matching property is found. select-property (别名selp )是一种选择(类似于 select-object)非公共属性的函数 - 但是,如果只找到一个匹配的属性,它会自动像-ExpandProperty一样-ExpandProperty (I wrote select-property and invoke-method at work, so can't share the source code of those). (我在工作中写了select-propertyinvoke-method ,所以不能分享它们的源代码)。

# 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
}

EDIT: per mklement0's comment:编辑:根据 mklement0 的评论:

Here is a link to the Nivot ink blog on a script on the "poke" module which similarly gives access to non-public members.这是“poke”模块上脚本上的 Nivot 墨水博客的链接,它同样允许非公开成员访问。

As far as additional comments, I don't have meaningful ones at this point.至于其他评论,我目前没有有意义的评论。 This code just mimics what a decompilation of select-object reveals.这段代码只是模仿了反编译select-object揭示的内容。 The original MS comments (if any) are of course not in the decompilation.原始的 MS 注释(如果有)当然不在反编译中。 Frankly I don't know the purpose of the various types the function uses.坦率地说,我不知道该函数使用的各种类型的用途。 Getting that level of understanding would likely require a considerable amount of effort.获得这种理解水平可能需要付出相当大的努力。

My suggestion: get Oisin's poke module.我的建议:获取 Oisin 的 poke 模块。 Tweak the code to use that module.调整代码以使用该模块。 And then try it out.然后尝试一下。 If you like the way it works, then use it and don't worry how it works (that's what I did).如果你喜欢它的工作方式,那就使用它,不要担心它是如何工作的(这就是我所做的)。

Note: I haven't studied "poke" in any depth, but my guess is that it doesn't have anything like -returnClosure .注意:我没有深入研究过“戳”,但我的猜测是它没有像-returnClosure这样的-returnClosure However adding that should be easy as this:但是添加应该很容易,因为:

if (-not $returnClosure) {
  $methodInfo.Invoke($arguments)
} else {
  {$methodInfo.Invoke($arguments)}.GetNewClosure()
}

Try these filters, they'll force the pipeline to stop after the first object -or the first n elements- and store it -them- in a variable;试试这些过滤器,它们会强制管道在第一个对象 - 或前 n 个元素 - 之后停止,并将它 -them- 存储在一个变量中; you need to pass the name of the variable, if you don't the object(s) are pushed out but cannot be assigned to a variable.您需要传递变量的名称,如果您没有将对象推出但不能分配给变量。

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

Here's an - imperfect - implementation of a Stop-Pipeline cmdlet (requires PS v3+), gratefully adapted from this answer :这是一个不完美的Stop-Pipeline cmdlet 实现(需要 PS v3+),非常感谢地改编自这个答案

#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

Caveat : I don't fully understand how it works, though fundamentally it takes advantage of Select -First 's ability to stop the pipeline prematurely (PS v3+).警告:我并不完全理解它是如何工作的,尽管从根本上说它利用了Select -First过早停止管道的能力(PS v3+)。 However, in this case there is one crucial difference to how Select -First terminates the pipeline: downstream cmdlets (commands later in the pipeline) do not get a chance to run their end blocks.然而,在这种情况下,是如何一个关键的不同Select -First终止管道:下游的cmdlet(后来在管道命令)没有得到一个机会来运行他们的end块。
Therefore, aggregating cmdlets (those that must receive all input before producing output, such as Sort-Object , Group-Object , and Measure-Object ) will not produce output if placed later in the same pipeline ;因此,聚合 cmdlet (在产生输出之前必须接收所有输入的那些,例如Sort-ObjectGroup-ObjectMeasure-Object如果稍后放置在同一管道中将不会产生输出 eg:例如:

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

Background info that may lead to a better solution:可能导致更好解决方案的背景信息:

Thanks to PetSerAl , my answer here shows how to produce the same exception that Select-Object -First uses internally to stop upstream cmdlets.感谢PetSerAl ,我在这里的回答显示了如何产生与Select-Object -First内部用于停止上游 cmdlet 相同的异常。

However, there the exception is thrown from inside the cmdlet that is itself connected to the pipeline to stop , which is not the case here:但是,异常是从本身连接到管道到 stop的 cmdlet 内部抛出的,这里不是这种情况:

Stop-Pipeline , as used in the examples above, is not connected to the pipeline that should be stopped (only the enclosing ForEach-Object ( % ) block is), so the question is: How can the exception be thrown in the context of the target pipeline?上面例子中使用的Stop-Pipeline没有连接到应该停止的管道(只有封闭的ForEach-Object ( % ) 块),所以问题是:如何在上下文中抛出异常目标管道?

Another option would be to use the -file parameter on a switch statement.另一种选择是在switch语句上使用-file参数。 Using -file will read the file one line at a time, and you can use break to exit immediately without reading the rest of the file.使用-file将一次读取文件一行,您可以使用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" }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM