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

时间:2008-11-10 17:20:18

标签: powershell pipeline

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

8 个答案:

答案 0 :(得分:5)

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

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

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

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

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

答案 1 :(得分:3)

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

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

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

答案 2 :(得分:3)

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

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

看看这篇关于如何终止管道的帖子:
http://powershell.com/cs/blogs/tobias/archive/2010/01/01/cancelling-a-pipeline.aspx

答案 3 :(得分:2)

不确定您的具体需求,但是值得您花些时间查看Log Parser,看看您是否无法使用查询在数据到达管道之前对其进行过滤。

答案 4 :(得分: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

答案 5 :(得分:1)

如果你愿意使用非公开成员,这是一种阻止管道的方法。它模仿nunits的作用。 select-object(别名invoke-method)是一个调用非公共方法的函数。 im(别名select-property)是一个选择(类似于select-object)非公共属性的函数 - 但如果只找到一个匹配属性,它会自动像selp一样运行。 (我在工作中写了-ExpandPropertyselect-property,所以不能分享那些的源代码。

invoke-method

编辑:根据mklement0的评论:

以下是关于“戳”模块的脚本的Nivot墨水博客的link,它同样可以访问非公开成员。

就其他评论而言,此时我没有有意义的评论。此代码只是模仿# 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 } 的反编译显示的内容。原始的MS注释(如果有的话)当然不在反编译中。坦率地说,我不知道函数使用的各种类型的目的。达到这种程度的理解可能需要相当多的努力。

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

注意:我没有深入研究“戳”,但我的猜测是它没有select-object之类的东西。但是添加它应该很简单:

-returnClosure

答案 6 :(得分:0)

另一种选择是在-file语句中使用switch参数。使用-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" }
}

答案 7 :(得分:0)

这是一个 - 不完美 - 实现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 +) 。但是,在这种情况下,Select -First如何终止管道有一个至关重要的区别: 下游 cmdlet(管道中的命令以后)无法获得有机会运行end块。
因此,聚合cmdlet (在生成输出之前必须接收所有输入的那些,例如Sort-ObjectGroup-ObjectMeasure-Object)如果以后放在同一个管道中,将不会产生输出; e.g:

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

可能带来更好解决方案的背景信息:

感谢PetSerAl,我的answer here显示了如何生成Select-Object -First内部用于停止上游cmdlet的相同异常。

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

如上例所示,

Stop-Pipeline 连接到应停止的管道(仅限封闭的ForEach-Object%)块是),所以问题是:如何在目标管道的上下文中抛出异常?