日付が指定された開始日と終了日の間にある場合、現在のオブジェクトをパイプラインにプッシュする単純な PowerShell フィルターを作成しました。パイプラインを通過するオブジェクトは常に日付の昇順であるため、日付が指定された終了日を超えるとすぐに作業が完了したことがわかり、上流のコマンドが作業を放棄できることをパイプラインに伝えて、パイプラインがその仕事を終えることができます。いくつかの非常に大きなログ ファイルを読んでいて、ログの一部だけを調べたいと思うことがよくあります。これは不可能だと確信していますが、念のためお願いしたいと思います。
8 に答える
外部ループを中断したり、スクリプトの実行を完全に停止させたりする (例外をスローするなど) ことで、パイプラインを中断することができます。解決策は、パイプラインを停止する必要がある場合に中断できるループでパイプラインをラップすることです。たとえば、次のコードは、パイプラインから最初の項目を返し、外側の do-while ループを中断してパイプラインを中断します。
do {
Get-ChildItem|% { $_;break }
} while ($false)
この機能は、次のような関数にラップできます。最後の行は上記と同じことを実現します。
function Breakable-Pipeline([ScriptBlock]$ScriptBlock) {
do {
. $ScriptBlock
} while ($false)
}
Breakable-Pipeline { Get-ChildItem|% { $_;break } }
ダウンストリーム コマンドからアップストリーム コマンドを停止することはできません。基準に一致しないオブジェクトを除外し続けますが、最初のコマンドは、処理するように設定されたすべてを処理します。
回避策は、上流のコマンドレットまたは関数/フィルターでさらにフィルター処理を行うことです。ログ ファイルを操作すると少し複雑になりますが、おそらく Select-String と正規表現を使用して、望ましくない日付を除外することができます。
取りたい行数と行数がわからない場合は、パターンをチェックするためにファイル全体が読み取られます。
パイプラインの終了時に例外をスローできます。
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
非パブリック メンバーを使用する場合は、ここでパイプラインを停止する方法があります。それは何をするかを模倣select-object
します。invoke-method
(エイリアスim
) は非パブリック メソッドを呼び出す関数です。select-property
(alias selp
) は (select-object に似た) 非パブリック プロパティを選択する関数ですが、-ExpandProperty
一致するプロパティが 1 つだけ見つかった場合は自動的に動作します。(私が書いselect-property
てinvoke-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 インク ブログへのリンクです。
追加のコメントに関しては、現時点では意味のあるものはありません。select-object
このコードは、 の逆コンパイルによって明らかになるものを模倣しています。元の MS コメント (もしあれば) はもちろん、逆コンパイルにはありません。率直に言って、関数が使用するさまざまな型の目的がわかりません。そのレベルの理解を得るには、かなりの労力が必要になるでしょう。
私の提案: Oisin の poke モジュールを入手してください。そのモジュールを使用するようにコードを微調整します。そして、それを試してみてください。それが機能する方法が気に入ったら、それを使用してください。
注: 私は「突く」について深く研究したことはありませんが、私の推測では、-returnClosure
. ただし、次のように簡単に追加できます。
if (-not $returnClosure) {
$methodInfo.Invoke($arguments)
} else {
{$methodInfo.Invoke($arguments)}.GetNewClosure()
}
これは、コマンドレットの不完全な実装ですStop-Pipeline
(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
ただし、この場合、 がパイプラインを終了する方法に決定的な違いが 1 つあります。下流のコマンドレット (パイプラインの後半end
にあるコマンド) は、ブロックを実行する機会がありません。
したがって、集約コマンドレット(出力を生成する前にすべての入力を受け取る必要があるコマンドレットSort-Object
、、、Group-Object
および などMeasure-Object
)は、後で同じパイプラインに配置された場合、出力を生成しません。例えば:
# !! NO output, because Sort-Object never finishes.
1..5 | % { if ($_ -gt 2) { Stop-Pipeline }; $_ } | Sort-Object
より良い解決策につながる背景情報:
PetSerAlのおかげで、ここでの私の回答Select-Object -First
は、上流のコマンドレットを停止するために内部的に使用するのと同じ例外を生成する方法を示しています。
ただし、パイプラインに接続されているコマンドレット内から stop に例外がスローされますが、ここではそうではありません。
Stop-Pipeline
、上の例で使用されているように、停止する必要があるパイプラインに接続されていないForEach-Object
(囲んでいる( %
) ブロックのみ)。
正確なニーズについてはわかりませんが、 Log Parserを調べて、データがパイプに到達する前にクエリを使用してデータをフィルター処理できないかどうかを確認する価値があるかもしれません。
これらのフィルターを試してみてください。最初のオブジェクト (または最初の 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
別のオプションは、ステートメントで-file
パラメーターを使用することです。switch
を使用-file
すると、一度に 1 行ずつファイルが読み取られ、 を使用する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" }
}