Powershell 如何将无限while循环转换为管道语句

Powershell 如何将无限while循环转换为管道语句,powershell,pipeline,Powershell,Pipeline,我正在尝试使用PowerShell管道执行一些重复性任务和检查,如 在管道中的响应将具有不同的状态后,执行某些检查X次或向前跳过 我可以编写的执行此类检查的最简单脚本如下: do { $result=Update-ACMEIdentifier dns1 -ChallengeType dns-01 if($result.Status -ne 'pending') { "Hurray" break } "Still pen

我正在尝试使用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
那么,有没有简单的一行程序,可以让我把无限对象生成器放在管道的一侧


此类对象的一个很好的例子可能是每13秒生成一个当前时间戳到管道中的勾号生成器

不确定在这种情况下为什么要在循环上使用管道,但可以使用一点C#;e、 g

$Source=@”
使用System.Collections.Generic;
公共静态类计数器
{
公共静态bool Running=false;
公共静态IEnumerable运行()
{
运行=真;
(跑步时)
{
for(long l=0;l在对问题的评论中给出了关键指针:包含无限循环的脚本块,通过调用运算符(
&
)调用,创建可以通过管道发送的无限对象源

& { while ($true) { Get-Date; Start-Sleep 1 } } | Select-Object -First 5
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 } }
&{while($true){…}

之后的管段可以根据需要停止管道

注意

& { while ($true) { Get-Date; Start-Sleep 1 } } | Select-Object -First 5
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 } }
  • 从PS v5开始,只有
    选择对象
    能够直接停止管道

    • 一个不完善的通用管道停止函数可以在我的例子中找到
  • 使用
    中断
    来停止管道是很棘手的,因为它不仅停止管道,而且会中断任何封闭循环-安全使用需要将管道包装在虚拟循环中。

  • 或者,布尔变量可用于终止无限生产者

以下是演示每种方法的示例:


使用
选择对象-第一个工作示例

& { while ($true) { Get-Date; Start-Sleep 1 } } | Select-Object -First 5
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 } }
这将无限期地每秒执行一次
Get Date
,但在5次迭代后被
Select Object
停止

具有
中断
和虚拟循环
的等效示例:

& { while ($true) { Get-Date; Start-Sleep 1 } } | Select-Object -First 5
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 } }
具有终止无限生产者的布尔变量的等效示例:

& { while ($true) { Get-Date; Start-Sleep 1 } } | Select-Object -First 5
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对象中(
%
)cmdlet的(隐式)中初始化
-Begin
block-初始化仍然发生在第一个管道段(无限生产者)开始执行之前。再次感谢@PetSerAl.

为什么它必须是管道语句?在第一个示例中,
}while($result.Status-ne'pending'))
可以运行到您需要的任何时间,而无需中断。在您的第一个示例中,您不是无限运行。您有一个基于条件的退出策略。
1..Infinity
->
&{for($i=1;;+$i){$i}
#这里的一些代码用于停止infinity producer或停止管道
->
选择Object-First 1
@matt well,因为我想将它们作为一行代码来编写,以便启动并忘记(并不时回到那个控制台。我的第一个示例并不完美,因为它只是显示了我想从脚本样式转换为管道表达式的行为。@PetSerAl很好,我还没有考虑过。将其作为注释发布,我接受。以稍微修改的样式,它解决了我所有的需要。
&{do{Start Sleep-Seconds 1;Get Date}while($true)}选择Object-First 5
.Hat tip to your.FWIW,使用
System.Management.Automation.ScriptBlock.GetSteppablePipeline()
您可以创建具有不同拓扑结构的管道,而不仅仅是直线。我创建了一个带有t形三通的管道:一个输入和两个输出。理论上,您可以创建一个圆形管道。单个对象可以循环,直到达到停止条件。
select-first 1
习惯用法不起作用,但您可以使用特殊的l表示您的函数(驱动管道)寻找的,以表示停止。好吧,它实际上不是一行。@AlexeyShcherbak如果使用分号,任何东西都可以是一行。这有什么关系+1@Matt:也许AlexeyShcherbak的意思是:它不是一个单独的语句(管道).我的问题根本不是关于如何做这件事,而是更多的是关于-如何在管道中以优雅的方式做这件事。我可能在问题的第一行中没有非常明确地表达这一点,但我提到了两次。因为这类语句的目的是一次性监视和检查-我不想编写比实际需要更多的代码ed.@PetSerAl的评论对我来说是比建议的.NET类更好的答案,为了无限循环这样简单的事情,他在CLR和Powershell之间来回跳跃。如果@PetSerAl不添加他的评论作为答案,我会接受这个答案。但我仍然认为他的答案准确地指出并解决了所描述的问题,rath比约翰·贝文的方法更有效。没有必要将
while(){}
更改为
do{}while()
&{Write Host second}{124;%{Write Host first}{}