我试图将PowerShell管道用于某些重复性任务和检查,例如 执行某些检查X次或在管道中的响应具有不同状态后向前跳过。
我可以编写的最简单的脚本来执行此类检查:
do {
$result=Update-ACMEIdentifier dns1 -ChallengeType dns-01
if($result.Status -ne 'pending')
{
"Hurray"
break
}
"Still pending"
Start-Sleep -s 3
} while ($true)
问题是 - 如何将此脚本编写为单个管道。 看起来我唯一需要的是无限管道来开始:
1..Infinity |
%{ Start-Sleep -Seconds 1 | Out-Null; $_ } |
%{Update-ACMEIdentifier dns1 -ChallengeType dns-01 } |
Select -ExpandProperty Status | ?{$_ -eq 'pending'} |
#some code here to stop infinity producer or stop the pipeline
那么是否有任何简单的单行,允许我将无限对象生成器放在管道的一侧?
此类对象的良好示例可能是 tick生成器,每 13 秒生成当前时间戳到管道中
答案 0 :(得分:2)
@PetSerAl在对该问题的评论中给出了关键指针:包含无限循环的脚本块,使用调用运算符(&
)调用,创建了无限的对象源,可以通过管道发送:
& { while ($true) { ... } }
以后的管道段可以根据需要停止管道。
注意强>:
自PS v5起,只有Select-Object
才能直接停止管道。
使用break
停止管道非常棘手,因为它不会停止管道,但会突破任何封闭循环 - 安全使用需要在虚拟循环中包裹管道。
或者,布尔变量可用于终止无限生成器。
以下是演示每种方法的示例:
Select-Object -First
工作示例:
& { while ($true) { Get-Date; Start-Sleep 1 } } | Select-Object -First 5
这会无限期地每秒执行Get-Date
,但在5次迭代后被Select-Object
停止。
break
和虚拟循环的等效示例:
do {
& { while ($true) { Get-Date; Start-Sleep 1 } } |
% { $i = 0 } { $_; if (++$i -eq 5) { break } } # `break` stops the pipeline and
# breaks out of the dummy loop
} while ($false)
使用布尔变量终止无限生成器的等效示例:
& { while (-not $done) { Get-Date; Start-Sleep 1 } } |
% { $done = $false; $i = 0 } { $_; if (++$i -eq 5) { $done = $true } }
请注意即使$done
仅在第二个管道段中初始化 - 即在ForEach-Object
(%
)cmdlet中(隐式) )-Begin
阻止 - 在第一个管道段 - 无限生产者 - 开始执行之前,初始化仍然发生。再次感谢@PetSerAl。
答案 1 :(得分:1)
在这种情况下,不确定为什么要在循环中使用管道,但可以使用一些C#; e.g。
$Source = @"
using System.Collections.Generic;
public static class Counter
{
public static bool Running = false;
public static IEnumerable<long> Run()
{
Running = true;
while(Running)
{
for (long l = 0; l <= long.MaxValue; l++)
{
yield return l;
if (!Running) {
break;
}
}
}
}
}
"@
Add-Type -TypeDefinition $Source -Language CSharp
[Counter]::Run() | %{
start-sleep -seconds 1
$_
} | %{
"Hello $_"
if ($_ -eq 12) {
[Counter]::Running = $false;
}
}
注意:因为数字是与管道执行并行生成的,所以生成器可能会在停止之前创建积压的数字。在我的测试中没有发生;但我相信这种情况是可能的。
你还会注意到我在while循环中遇到了一个for循环;这是为了确保所产生的价值是有效的;即所以我不会超出数据类型的最大值。
<强>更新强>
Per @ PetSerAl上面的评论,这是纯PowerShell中的改编版本:
$run=$true; &{for($i=0;$run;$i++){$i}} | %{ #infinite loop outputting to pipeline demo
"hello $_";
if($_ -eq 10){"stop";$run=$false <# stopping condition demo #>}
}