【问题标题】:Powershell build step, fire and forget?Powershell 构建步骤,一发不可收拾?
【发布时间】:2018-08-07 14:39:05
【问题描述】:

我在使用 TFS 2018 的构建步骤中运行以下 powershell 命令。

Start-Job -ScriptBlock {
    Invoke-Command -FilePath \\MyServer\run.ps1 -ComputerName MyServer -ArgumentList arg1, arg2
}

由于我不希望脚本影响构建步骤,它应该简单地触发并忘记脚本。因此我使用Start-Job。但似乎一旦完成该步骤,该过程就会被终止。即使构建步骤已完成或构建过程已完成,是否有办法维持进程生命周期?

其他信息... powershell 脚本应该在远程服务器上运行。脚本本身会触发带有参数的 .exe。

【问题讨论】:

    标签: powershell tfs azure-devops jobs


    【解决方案1】:

    要简单地触发并忘记,请使用Invoke-Command -AsJob 调用脚本:

    Invoke-Command -AsJob -FilePath \\MyServer\run.ps1 -ComputerName MyServer -Args arg1, arg2
    Start-Sleep 1 # !! Seemingly, this is necessary, as @doorman has discovered.
    
    • 这应该以异步方式远程启动脚本,并在本地会话中创建一个作业以监控其执行。

      • 警告:使用Start-Sleep - 可能需要更长的等待时间 - 为了在调用脚本退出之前创建远程进程,这似乎是必要的,但这样的解决方案可能不完全可靠,因为没有保证时间。
    • 由于您不打算监控远程执行,因此 本地 会话终止 - 以及随之而来的监控作业 - 应该无关紧要。

    【讨论】:

    • 嗨@mklement0 毕竟这行得通。出于某种原因,我只需要在 Invoke-Command 之后立即添加一秒钟的延迟。也许构建步骤在远程 cmd 脚本运行之前完成。所以在 Invoke-Command 之后添加 Start-Sleep 1 解决了这个问题。
    • @doorman:谢谢你告诉我;很高兴您找到了解决方案,并且我已经更新了答案,但我想知道为什么需要 Start-Sleep 以及是否有更强大的替代方案。
    【解决方案2】:

    您希望脚本何时停止运行?您可以使用do-while 循环并提出满足您需求的<condition>

    Start-Job -ScriptBlock {
        do{
            Invoke-Command -FilePath \\MyServer\run.ps1 -ComputerName MyServer -ArgumentList arg1, arg2
            Start-Sleep 2
        }while(<condition>)
    }
    

    或者,您可以使用条件$true,使其永远执行。当您不再需要该作业时,您必须稍后在脚本中停止它。

    $job = Start-Job -ScriptBlock {
        do{
            Invoke-Command -FilePath \\MyServer\run.ps1 -ComputerName MyServer -ArgumentList arg1, arg2
            Start-Sleep 2
        }while($true)
    }
    
    Stop-Job $job
    Remove-Job $job
    

    我添加了一个Start-Sleep 2,因此它不会因为不知道脚本在做什么而锁定您的 CPU - 如果不需要,请删除。

    【讨论】:

    • 嗨@gsm0ulman 我只想执行一次脚本而不影响构建时间。所以它应该只运行一次并在完成后完成。该脚本正在远程服务器上运行。我希望远程服务器继续运行它,即使构建服务器上的构建过程(触发脚本)已完成运行。
    【解决方案3】:

    为什么不这样:

    Invoke-Command -Filepath \\MyServer\Run.ps1 -Computername MyServer -Argumentlist Arg1,Arg2 -AsJob
    $JobCount = (get-job).Count
    Do
    {
        Start-Sleep -Seconds 1
        $totalJobCompleted = (get-job | Where-Object {$_.state -eq "Completed"} | Where-Object {$_.Command -like "NAMEOFCOMMAND*"}).count
    }
    Until($totalJobCompleted -ge $JobCount)
    

    【讨论】:

    • 嗨@Jewtus 我在命令后添加了以下代码。缩短了变量,因为 TFS 允许有限的字符。 -AsJob $c = (get-job).Count Do { Start-Sleep -Seconds 1 $done = (get-job | Where-Object {$_.state -eq "Completed"} | Where-Object {$_. Command -like "NAMEOFCOMMAND*"}).count } Until($done -ge $c) ..... 当我运行它时,我收到以下错误 A positional parameter cannot be found that accepted argument '$null'跨度>
    • @doorman,删除 $_.Command 的 where 对象。我把它作为一个占位符。如果您查看返回结果,每个返回的作业都会有一个命令,因此如果您触发的不仅仅是这些作业,您可以过滤您正在执行的特定命令。
    • 嗨@Jewtus 好的,现在它运行了,但是构建步骤要等到工作完成运行。无论构建过程如何,我都希望构建继续进行并且作业运行。
    • 如果你想在本地机器上创建一个作业来监控它,你需要把这个命令保存为一个文件,然后使用invoke-command和文件来创建一个本地作业。然后你可以使用 Get-Job 命令来监控本地作业,其他机器仍然会触发。
    • 嗨@Jewtus,感谢您的回复。我不需要监视它,我认为可以在服务器上启动一个进程,而该进程不依赖于父进程的生命周期(触发它的进程)我在远程服务器上有一个 REST API我可以尝试从那里调用脚本并从构建服务器调用 REST API,不确定它是否有效。
    【解决方案4】:

    @门卫- PowerShell 本身就是一个单线程应用程序。在几乎所有情况下,这都是一个巨大的好处。即使强制使用多个线程,您也可以看到子线程始终依赖于主线程。如果不是这种情况,那么很容易造成内存泄漏。这几乎总是一件好事,因为当您关闭主线程时,.Net 将为您清理您可能忘记的所有其他线程。您只是碰巧遇到了这种行为对您的情况不利的情况。

    有几种方法可以解决这个问题,但最简单的方法可能是使用 good ol' 命令提示符来启动一个完全不基于原始脚本的独立新实例。为此,您可以将invoke-expression 与'cmd /c' 结合使用。见下文:

    invoke-expression 'cmd /c start powershell -NoProfile -windowstyle hidden -Command {
        $i = 0
        while ($true) {
            if($i -gt 30) {
                break
            }
            else {
                $i | Out-File C:\Temp\IndependentSessionTest.txt -Append
                Start-Sleep -Seconds 1
                $i++
            }
        }
    }
    '
    

    这将启动一个新会话,运行您想要的脚本,在脚本运行时不显示窗口并且不使用您的 powershell 配置文件。您将能够看到,即使您终止了原始 PowerShell 会话,该会话仍将继续运行。您可以通过在关闭主 powershell 窗口后查看 IndependentSessionTest.txt 文件来验证这一点,并看到该文件不断获得更新的数字。

    希望这会为您指明正确的方向。

    这里有一些源链接:

    PowerShell launch script in new instance

    How to run a PowerShell script without displaying a window?

    【讨论】:

    • 感谢您的详细解答。问题在于延迟。
    猜你喜欢
    • 1970-01-01
    • 2017-04-16
    • 2019-05-26
    • 1970-01-01
    • 2018-10-20
    • 1970-01-01
    • 2012-09-24
    • 2016-01-26
    • 2022-01-17
    相关资源
    最近更新 更多