16

我编写了一个简单的 PowerShell 过滤器,如果它的日期在指定的开始日期和结束日期之间,它会将当前对象推送到管道中。管道中的对象总是按日期升序排列,所以只要日期超过指定的结束日期,我就知道我的工作已经完成,我想告诉管道上游命令可以放弃他们的工作,以便管道可以完成它的工作。我正在阅读一些非常大的日志文件,并且我经常只想检查日志的一部分。我很确定这是不可能的,但我想问一下。

4

8 回答 8

9

可以使用任何会破坏外部循环或完全停止脚本执行(如抛出异常)的东西来破坏管道。然后解决方案是将管道包装在一个循环中,如果您需要停止管道,您可以中断该循环。例如,以下代码将返回管道中的第一项,然后通过中断外部 do-while 循环来中断管道:

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

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

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

Breakable-Pipeline { Get-ChildItem|% { $_;break } }
于 2015-06-19T17:29:19.090 回答
3

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

解决方法是对上游 cmdlet 或函数/过滤器进行更多过滤。使用日志文件会使事情变得更加复杂,但也许使用 Select-String 和正则表达式来过滤掉不需要的日期可能对您有用。

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

于 2008-11-10T17:31:58.493 回答
3

结束管道时可以抛出异常。

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 -管道.aspx

于 2012-03-19T08:09:58.643 回答
2

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

于 2008-11-10T20:00:24.323 回答
2

如果您愿意使用非公共成员,这是一种停止管道的方法。它模仿了什么select-objectinvoke-method(别名im)是调用非公共方法的函数。select-property(alias selp) 是一个选择(类似于 select-object)非公共属性的函数 - 但是-ExpandProperty如果只找到一个匹配的属性,它会自动运行。(我写的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
}

编辑:根据 mklement0 的评论:

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

至于其他评论,我目前没有有意义的评论。这段代码只是模仿了反编译所select-object揭示的内容。原始的 MS 注释(如果有的话)当然不在反编译中。坦率地说,我不知道函数使用的各种类型的目的。获得这种程度的理解可能需要大量的努力。

我的建议:获取 Oisin 的 poke 模块。调整代码以使用该模块。然后尝试一下。如果你喜欢它的工作方式,那么就使用它,不要担心它是如何工作的(这就是我所做的)。

注意:我没有深入研究“poke”,但我猜它没有类似-returnClosure. 但是添加应该很容易,因为:

if (-not $returnClosure) {
  $methodInfo.Invoke($arguments)
} else {
  {$methodInfo.Invoke($arguments)}.GetNewClosure()
}
于 2016-02-19T18:26:22.203 回答
2

这是一个不完美的Stop-Pipelinecmdlet 实现(需要 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

警告:我不完全理解它是如何工作的,尽管它从根本上利用了Select -First' 过早停止管道的能力(PS v3+)。Select -First但是,在这种情况下,终止管道的方式有一个关键区别:下游cmdlet(管道中稍后的命令)没有机会运行它们的end块。
因此,聚合 cmdlet(必须在生成输出之前接收所有输入的命令,例如Sort-ObjectGroup-ObjectMeasure-Object如果稍后放置在同一管道中,则不会生成输出;例如:

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

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

感谢PetSerAl,我在这里的回答显示了如何产生相同的异常,该异常在Select-Object -First内部使用来停止上游 cmdlet。

但是,从本身连接到要停止的管道的 cmdlet 内部抛出异常,这里不是这种情况:

Stop-Pipeline,如上面示例中使用的,没有连接到应该停止的管道(只有封闭的ForEach-Object( %) 块是),所以问题是:如何在目标管道的上下文中抛出异常?

于 2016-01-16T22:01:28.170 回答
1

试试这些过滤器,它们会强制管道在第一个对象(或前 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
于 2008-11-10T18:52:37.127 回答
0

另一种选择是在语句中使用-file参数。switchusing-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" }
}
于 2008-11-16T18:46:48.393 回答