PowerShell后台作业解锁脚本性能
在当今复杂且不断扩展的IT环境中,没有人有时间等待PowerShell脚本缓慢执行完成,尤其是在时效性至关重要的情况下。作为希望广泛使用PowerShell自动化的管理员,您会希望编写能够轻松处理复杂任务的高级脚本。有时,由于各种原因性能较慢,等待脚本完成几乎违背了编写脚本的初衷。虽然像浏览器标签页一样打开多个PowerShell控制台并不一定不好,但如果您因为脚本速度慢而这样做,那就是一个问题。幸运的是,PowerShell有一个称为后台作业的功能,可以并行运行脚本和命令。这使您能够在后台执行长时间运行的任务,而不影响当前控制台。让我们更深入地了解PowerShell后台作业是什么,如何开始使用和管理它们,以及使用示例。
什么是PowerShell中的作业?
在所有情况下,作业都是PowerShell异步运行的任务,这意味着它运行命令或脚本而不影响提示符的可用性。PowerShell有几种后台作业类型:
- RemoteJob:通过远程会话运行的命令和脚本。
- ThreadJob:在与父PowerShell进程相同进程的单独线程中运行的命令和脚本。
- BackgroundJob:在与父PowerShell进程分开的进程中运行的命令和脚本。
选择ThreadJob和BackgroundJob取决于进程隔离和性能。在同一进程中运行的脚本,即使是不同的线程,也可能影响父进程。如果ThreadJob遇到终止错误,则可能影响父进程。虽然BackgroundJob不是这种情况,但在单独进程中运行确实会增加一些开销,包括对象的处理方式,因为您无法从BackgroundJob接收实时对象。
通常,由于BackgroundJob更全面,它们是推荐的作业类型,并且是本文其余部分所讨论的内容。
管理PowerShell后台作业的基础知识
PowerShell后台作业管理是通过Microsoft.PowerShell.Core插件中的cmdlet完成的。查看可用命令的最简单方法是使用Get-Command查询所有以-Job结尾的命令,使用Format-Table别名以获得更清晰的输出。
|
|
这是PowerShell 7.5的输出。
这是Windows PowerShell的输出。
仔细检查后,您会发现Resume-Job和Suspend-Job在PowerShell 7.5中不再可用,但本教程提供了适用于Windows PowerShell和PowerShell 7的示例。
PowerShell的一个优点是您可以从cmdlet名称推断它们的功能。想要启动一个作业?让我们试试Start-Job。
|
|
没有Write-Host输出,而是返回一个作业对象。后台作业异步运行,需要特定命令来检查其状态并查找其输出。如果您没有将该命令的输出分配给变量,如本例所示,请注意Id。使用Get-Job查找作业信息。
|
|
现在,您可以看到状态标记为Completed。
要从作业接收数据,请检查HasMoreData属性是否为True,表示有可用输出。使用Receive-Job收集该数据,将输出分配给变量。
|
|
在这种情况下,数据只是Write-Host的输出。$jobOutput变量为空。
如果您需要等待作业完成,可以使用哪个命令?让我们试试Wait-Job。
|
|
PowerShell暂停,直到作业完成。在这种情况下,它立即返回。但是,由于您在PowerShell中,可以使用管道将每个命令的输出传递给下一个命令,以形成一个简洁的一行命令。
|
|
当您需要特定作业完成后再执行下一个命令时,Wait-Job cmdlet非常有用。
最后,要删除作业,请使用Remove-Job。
|
|
任何剩余的作业在PowerShell会话关闭时都会被删除。
后台作业与Start-Process的区别
如果您认为后台作业是Start-Process的一个花哨包装,您并不完全错误。虽然它们都运行与主PowerShell会话分开的进程,但后台作业可以继续与父会话交互以检查状态和执行其他操作。Start-Process没有相同的功能。
复制前面的示例,尝试以下命令——在Windows PowerShell中将pwsh替换为powershell。
|
|
运行该命令会瞬间打开和关闭PowerShell控制台,没有留下任何是否工作的指示。
使用Start-Process复制后台作业需要大量工作来构建进程的标准输入/输出重定向,然后执行对象序列化以加载回原始父PowerShell进程。所有这些努力都是多余的,以重新创建后台作业中已经存在的功能。当启动GUI应用程序或使用不同凭据运行进程时,您可能更喜欢Start-Process而不是后台作业,以实现与父PowerShell会话的进程隔离。
如何在脚本中使用后台作业
将您的PowerShell代码转换为作为后台作业运行可能需要一些努力。后台作业不提供对父会话的交互式输入。例如,检查以下命令中的作业会给出Blocked状态。
|
|
要将数据传递给作业,您可以使用-ArgumentList参数和具有param()块的scriptblock。
|
|
此屏幕截图显示-ArgumentList参数中的字符串输出被传递并由作业处理。
还需要考虑错误处理。如果发生停止错误,作业将失败。例如,如果您运行以下简单脚本,然后查询作业,它会返回Failed状态。
|
|
因此,处理任何错误并仅在需要时强制脚本停止错误非常重要。
使这个过程 significantly easier的一种方法是在交互式会话中调试脚本,注意输入、输出和错误,然后添加步骤以记录到文件以跟踪执行信息。日志记录可以包括简单的检查点以表示脚本的哪些部分已执行,或者可以采取更全面的方法,记录错误和对象属性。
如何处理PowerShell后台作业的数据
PowerShell不是一种强类型编程语言。这可能不是问题,但在不同的执行上下文中工作时需要注意这一点。从后台作业返回的对象经过序列化和反序列化过程,这会影响功能。
从后台作业接收数据几乎与从PowerShell中的任何其他cmdlet接收数据一样容易。使用Receive-Job收集数据输出。例如,以下PowerShell命令获取文件对象。
|
|
返回的对象看起来像合法的FileInfo对象,但使用以下代码仔细检查类型。
|
|
如果您在交互式会话中执行相同的操作,则会获得预期的类型。
|
|
结果不同,因为PowerShell序列化对象,然后在从后台作业返回时反序列化对象。属性或数据被保留,但方法——使用括号调用的操作或函数——不可用。
例如,这些命令在交互式会话中有效,但如果您从作业接收FileInfo对象,则无效,如此处所示。
后台作业非常适合与仅数据对象(如PSCustomObject)一起使用。
如何扩展PowerShell后台作业
虽然在后台运行单个PowerShell任务很有帮助,但真正的好处是通过同时运行多个后台作业来实现的,尤其是在时间紧迫的情况下。
例如,也许您需要尽快在所有服务器上运行gpupdate以应用组策略更改。使用foreach循环且没有后台作业,此任务在每个服务器上顺序运行,如以下PowerShell脚本所示。
|
|
该脚本顺序处理每个服务器,在移动到下一个服务器之前在一个服务器上执行更新。对于拥有数百或数千台服务器的企业,此过程可能需要很长时间。
但是,使用后台作业,PowerShell可以快速完成此操作。
第一个想法可能是在Invoke-Command上使用-AsJob参数,最终得到以下结果。
|
|
由于作业开销和服务器数量的原因,此脚本可能会使本地机器过载。您可以通过使用以下命令监视正在运行的作业数量来管理此问题:
|
|
可以同时运行的作业数量因主机系统而异,但对于像这样的简单脚本,一个好的起点可能是20。让我们相应地更新脚本,如果计数为20或更多,则让其睡眠五秒钟,以防止系统过载。
|
|
现在,您有一个解决方案,可以快速且可扩展的方式将组策略更改应用于AD中的所有服务器。在您的环境中运行这样的脚本之前,请正确获取服务器来源。例如,您可能有一个组织单位,可以与-SearchBase参数一起使用以针对特定服务器而不是所有服务器。
请注意,使用带有-AsJob参数的Invoke-Command在技术上启动的是RemoteJob,而不是BackgroundJob。但是,RemoteJob的处理方式与BackgroundJob相同,因此Get-Job、Wait-Job和Receive-Job仍然适用于此方法。
如何使用后台作业大规模处理数据
运行许多作业后,跟踪每个作业的状态可能令人生畏,尤其是在前面的示例中,脚本在AD中的每个服务器上运行命令。要按作业跟踪状态,您可以通过多种方式为每个作业指定有意义的名称:
- Start-Job中的-Name参数。
- Invoke-Command中的-JobName参数。
- 作业本身的Location属性。
如果您正在运行一组针对特定资源、文件共享中的文件夹或网络上的IP地址的作业,则可以使用Start-Job中的-Name参数跟踪该信息。
|
|
运行Get-Job显示每个作业的共享名称。
或者,如果您正在使用RemoteJob,如gpupdate示例中,您可以使用-JobName参数或Location属性。例如,在远程系统上运行作业时,运行以下PowerShell命令后,Location属性中会出现远程主机的名称。
|
|
这使您能够轻松地以可扩展的方式运行每个作业状态的报告,例如以下示例。
|
|
如果您有长时间运行的作业,则可能需要等待它们完成后再运行报告。
Anthony Howell是一位IT策略师,在基础设施和自动化技术方面拥有丰富经验。他的专业知识包括PowerShell、DevOps、云计算以及在Windows和Linux环境中工作。