跨会话的Powershell任务?

4

我一直在探索Powershell中的作业(使用Start-Job、Get-Job、Receive-Job等命令)。

只要在同一个Powershell会话/窗口中,所有东西都可以正常工作,但是如果我打开新的Powershell窗口并执行“Get-Job”命令,它将不返回任何作业。

在Powershell中,难道没有任何方法可以启动后台作业、关闭窗口/会话,然后从另一个会话/窗口中访问它吗? 我有几个长时间运行的cmdlet,希望能够在后台启动并稍后检查,而无需保证使用相同的提示符。

3个回答

2

更新

像我承诺的那样,我为您找到了另一个潜在的解决方案,DaveUK。今天我了解到PowerShell中的“RunSpaces”。我认为这可能是我们都在寻找的东西。它需要更多的工作,但使用 线程安全集合。以下示例摘自Boe Prox的 在PowerShell Runspaces之间共享变量和实时对象。他的文章详细描述了它做什么,如何工作,甚至可以执行多个“作业”。

使用 Debug-Runspace,您可以从其他会话监视它。我将努力在此答案中提供更多详细信息,以防该网站消失。但现在,希望这可以帮助到您。

$hash = [hashtable]::Synchronized(@{})
$hash.value = 1
$hash.Flag = $True
$hash.Host = $host
Write-host ('Value of $Hash.value before background runspace is {0}' -f $hash.value) -ForegroundColor Green -BackgroundColor Black
$runspace = [runspacefactory]::CreateRunspace()
$runspace.Open()
$runspace.SessionStateProxy.SetVariable('Hash',$hash)
$powershell = [powershell]::Create()
$powershell.Runspace = $runspace
$powershell.AddScript({
    While ($hash.Flag) {
        $hash.value++
        $hash.Services = Get-Service
        $hash.host.ui.WriteVerboseLine($hash.value)
        Start-Sleep -Seconds 5
    }
}) | Out-Null
$handle = $powershell.BeginInvoke()

之前的回复

我已经尝试了几年来监视后台作业,而不依赖于主窗口保持打开,但是还没有找到一个非常规的解决方案。这让我相信使用内置的“-Job”功能不可能实现。因此,这并不是对主要问题的答案,而是我使用的一种解决方法。基本上,这是创建自己的“作业”设置,因为内置的功能不能按照我的想法运行。在这里需要注意的是,我很少在新进程中运行“单个作业”。通常是一个创建多个作业的进程。如果您需要长时间运行cmdlet,则可以将其启动到自己的PowerShell进程中,并将详细记录到文件中。

要在“后台进程”中运行多个作业,我会使用某种模板结构来构建您的cmdlet或作业。

  1. 将输出记录到文件中,而不是试图依靠从Receive-Job读取它。
  2. 实现一种在完成任务后停止cmdlet的方法,我通常会在启动作业时创建一个带有JobID的文件,如果JobID文件不存在,则结束作业。这样,如果您想要停止它,只需删除该JobID。我通常也会为它们分配名称,以便记住它是哪个作业。
  3. 如果您想要状态报告,请执行类似于“JobID”文件的操作,但是类似于JobID-Status,并且它将向日志文件添加一个包含状态的行,然后删除该文件。
  4. 最后,您必须单独启动作业进程,而不是使用当前所在的PowerShell窗口。使用Start-Process调用脚本来启动作业将使其从主窗口分离出来成为“后台”进程。请记住,您调用的脚本应在作业完成时退出PowerShell。例如:

示例Start-Process脚本:

$Jobs = @("Name1", "Name2")
ForEach ($JobName in $Jobs) {
    $Job = Start-Job -Name $JobName -ScriptBlock { 
         # Code Here either static file or from variable 
         # With logic to stop a job if the file is removed, and create the file
    } 
 }

 $JobsRunning = $true
 while ($JobsRunning) { 
        $JobsRunning = $false

        ForEach ($JobName in $Jobs) {
            if ((Get-Job -Name $JobName).State -ne "Completed") {
                $JobsRunning = $true
            }
        } 

        Start-Sleep -seconds 120 
    } 
}

exit

我没有你想要的完整解决方案,但经过几年的渴望和阅读 Google、M$、SO 等所有内容,我得出结论:不可能在 PowerShell 窗口会话之间传输作业。
我希望我是错误的,如果有人发帖说明如何做到,那就太好了。如果不行,上面概述的方法对我来说非常有效。虽然有些烦人并需要额外的工作,但它确实可行。

1
你有研究过计划任务吗?我发现这个功能非常有前途。虽然我不太关心触发器/调度方面,但它似乎提供了一种真正将作业放在后台的方法(无需关心会话)- https://devblogs.microsoft.com/scripting/introduction-to-powershell-scheduled-jobs/ - DaveUK
1
这是我从未遇到过的事情。从未想过在搜索中使用“调度”这个词。这是一个很好的发现,我明天会进行研究/测试 :) - Jim
是的,我运行了一个测试,似乎可以工作,但是使用任务调度程序感觉有些笨重。既然我已经写好了我的方法,我可能会坚持使用它,但知道有一个“可行”的解决方案还是很好的。感谢您发布这个。如果您回答了自己的问题,我会将其添加为答案 :) - Jim
1
哎呀...看起来在作业运行时没有可用的信息。所有的“进度”只有在作业完成后才会出现在作业对象中。 - DaveUK
啊,最终还是一样的,你必须编写自己的跟踪器。也许我看到过,只是不记得了,因为我知道我不会在没有测试任何可行性的情况下编写自己的结构。这仍然是一个不错的想法,你只需将输出写入日志文件,而不是等待它返回。 - Jim
显示剩余5条评论

1
我发现了PSScheduledJob模块,它似乎比普通的Job更适合解决我的问题:

https://learn.microsoft.com/en-us/powershell/module/psscheduledjob/?view=powershell-5.1

已安排的工作似乎可以在任何PS会话中找到并检索,因此您可以使用它作为触发“工作”的方式,即使在关闭PS窗口后仍将继续运行。

到目前为止,我发现唯一的问题是当ScheduledJob运行时,它会生成一个常规的Job,但该常规Job没有子Job,并且在完成之前没有可用的进度/状态。似乎不可能在运行时监视作业的状态/输出,因此这仍然不完美 :(


谢谢您的尝试,我可能在几年前尝试过,但已经记不清了。目前唯一的方法仍然是在运行时编写日志文件以进行监控。 - Jim

0

我建议您研究工作流程,因为它们被设计成具有强大的功能,可以处理长时间运行的任务,并且可以从故障/重新启动中恢复。但是,您也可以使用Invoke-Command-InDisconnectedSession在后台运行命令或脚本,并再次从同一台或不同的计算机连接到它。试试这个例子。

创建会话

注意-在我的测试中,本地运行需要管理员权限,而远程运行则不需要。

$opt = New-PSSessionOption -IdleTimeout 86400000
$log = 'c:\temp\workflowtest.txt'

Invoke-Command -InDisconnectedSession -ScriptBlock {
    1..10 | ForEach-Object {
        "Current number is $_" | Add-Content $using:log
        Start-Sleep -Seconds 1
        write-host current iteration $_ -ForegroundColor Green
    }
    Get-Content $using:log
} -SessionOption $opt -SessionName testsession -ComputerName $env:COMPUTERNAME

关闭你从中运行此步骤的窗口,作为一个良好的测试步骤。

接收输出

打开一个新的 PowerShell 窗口(以管理员身份),并检索输出。如果还没有完成,它将一直接收,直到完成为止。

Get-PSSession -ComputerName $env:computername | Receive-PSSession

移除会话

Get-PSSession -ComputerName $env:computername | Remove-PSSession

https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_remote_disconnected_sessions?view=powershell-5.1


谢谢Doug - 我正在研究这些。到目前为止,在我的场景中,我发现两者都有一些奇怪的行为。例如,当所有PS窗口关闭时,工作流(作为作业运行)似乎会被挂起,当我重新打开PowerShell时,我必须手动恢复它们。我认为到目前为止,PSScheduledJob的概念最接近我所寻找的。我只希望有一种好的方法来查看正在运行的计划作业的状态/消息,以便定期报告状态/消息。 - DaveUK
这里需要注意的一点是,默认会话限制为10个并发用户/每个用户25个。可以使用Get-ChildItem -Path WSMAN:\localhost\Shell进行检查,并根据您的环境进行增加。因此,如果他正在尝试同时在30台计算机上运行脚本,则需要提高限制。此外,断开连接取决于运行时间有多长,以及是否依赖VPN。 - Jim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接