当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] PowerShell 脚本:如何在重构中幸存

作者:精品下载站 日期:2024-12-14 13:03:05 浏览:13 分类:玩电脑

PowerShell 脚本:如何在重构中幸存


如果您曾经继承过一个脚本或一组 PowerShell 脚本,您可能知道这种挫败感。你有一种经过多年经验磨练出来的特定编码方式,你会遇到……让我们说是怪物。

你继承了你所见过的最丑陋、最可怕的撒旦 PowerShell 脚本。我是说这些脚本打破了书中的每一条规则。

不要被吓跑!一切都可以解决;甚至是自以为无所不知的新手编写的意大利面条脚本。

留在我身边。

构建 Pester 测试来验证环境变化

如果您读过 The Pester Book,您可能知道,您可以在各个“层”中构建测试。

  • 单元测试测试代码执行(考虑将哪些参数传递给函数、代码在 if/then 构造中遵循的分支等)。
  • 集成测试测试代码所做的更改。例如,测试 Set-Content 是否将适当的字符串添加到文件中,或者 New-Item 添加了预期的注册表项。如果脚本打开防火墙帖子,Get-NetFirewallRule 是否返回预期输出?
  • 测试最终目标的验收测试。如果您的脚本打开防火墙端口,您将尝试使用 Test-NetConnection 连接到该端口并验证服务是否正在运行。验收测试确认最终目标。

由于您最终将彻底更改代码,因此请跳过单元测试。同样,此时,您实际上并没有测试混乱代码是否执行了它应该执行的操作(原作者应该这样做)。

您需要构建一组广泛的集成测试来确认此代码所做的所有更改。您需要为每个注册表项或它更改的值、它移动的文件、它安装的更新等等一切构建测试!

为什么只进行集成测试?因为您必须确保一旦开始对原始代码库进行大量更改,您就有一个可以按下的按钮来确保您已获得所有内容。如果没有集成测试,您可以重构整个代码库,忘记 20 件不同的事情,您的代码不会产生错误,然后继续。

几个月后,您将遇到一个模糊的边缘情况,后来发现,您只是忘记在重构的代码中添加该异常。

将乱七八糟的东西收集到一处

就像近藤麻理惠所说,你首先需要把所有乱七八糟的东西收集起来,并把它们放到一个地方。整洁的 PowerShell 脚本总是给我带来快乐!如果混乱的内容被分成跨不同文件和文件夹的脚本,每个文件和文件夹都互相引用,请删除所有这些“链接”

将所有内容放入单个 PowerShell 脚本中。通过首先组合所有代码,您将能够更轻松地查找/替换字符串,而不必遵循脚本执行的兔子踪迹,并且您的理智将始终避免切换选项卡。

如果您遇到了由 10 个脚本调用其他调用模块函数的脚本组成的混乱情况,请将所有内容合并到一个 PS1 脚本中。在此阶段,您不关心保持可行的解决方案。只需将所有内容转储到一个脚本中即可。

使用区域

保留脚本中原始代码来源的某种历史记录的一种好方法是使用区域。让您创建代码“桶”。然后,您可以参考代码的来源来命名每个区域。

例如:

#region bad_script1.ps1

## Contents of bad_script1.ps1 here

#endregion

#region bad_script2.ps1

## Contents of bad_script2.ps1 here

#endregion

删除依赖项

接下来,继续尽可能地简化混乱脚本。为此,您需要删除尽可能多的依赖项。依赖关系包括代码中的参数、变量以及引用的任何文件、数据库等。

删除变量和参数

为 PowerShell 脚本中声明的所有变量赋值,或者完全删除它们。如果您正在使用可能包含数百个变量的脚本,则需要简化故障排除。您需要使其尽可能容易地遵循代码。变量和其他引用在成品中非常有用,但此时您需要简化代码。

删除所有参数,但一定要记录它们。您可能需要在最终产品中重新引入这些参数。

例如,如果您必须将许多 PowerShell 脚本合并在一起,那么您可能会有类似于以下代码片段的行:

#region bad script1
param(
	[Parameter()]
	[string]$Param1
)

$variable = 

## code here

#endregion

#region bad script2
param(
	[Parameter()]
	[string]$Param2
)

## code here

#endregion

删除所有参数块,并为这些变量分配一个静态值,如下所示。

[string]$ServerName = 'MYSWEETSNOWFLAKE'

## code here

#endregion

[string]$IpAddress = '192.168.0.1'

## code here

#endregion

拉近任何外部依赖关系

您无法删除所有依赖项,但请务必尽可能简化它们。例如,如果该脚本引用不同服务器上的各种文件,请将它们放入脚本所在的同一文件夹中。

如果 PowerShell 脚本正在查询数据库,请尝试运行本地副本。同样,删除依赖项的目的是简化您必须遵循的代码。

您的单一综合混乱脚本绝对不应该有任何参数,或者依赖于任何其他外部系统来运行。要测试它,您应该能够不带参数运行.\messscript.ps1,并且它应该由单一用例运行。

将所有日志记录/调试信息变成注释

简化混乱脚本的下一步是删除所有日志记录或调试代码。您可能会看到很多 Write-HostWrite-Verbose,甚至可能是自定义的 Write-Log 函数。要么注释掉它,要么完全删除它们。

您可以完全删除日志记录和调试行,但我建议将它们注释掉。您可能需要此信息来了解代码在某些时候正在做什么。一旦您熟悉了代码流程,您就可以删除注释。

日志记录和调试行对脚本的功能没有任何贡献。相反,他们使事情变得复杂,并且需要更长的时间来破译。

构建测试环境并使所有测试失败

无论原始解决方案对一堆虚拟机 (VM)、云资源还是其他任何内容进行更改,您都需要一个干净的测试环境。

构建一个测试环境,其中包含与正在发生的混乱情况不同的所有配置项。您需要一个包含与正在发生的混乱变化相反的一切的环境,以便您可以在事后进行比较。

构建测试环境后,运行集成测试套件。所有测试都失败了吗?完美的!您现在知道您的环境没有应用混乱代码应该更改的配置。

重要提示:不要忘记确保所有集成测试在您的测试环境中都失败。这个很重要!如果没有,一个配置项可能已经被应用,你将无法知道它是一开始就是这样还是你的重构代码更新了它。

您现在已经设置了一个原始环境来比较更改。

让混乱再次运转

现在,您应该将所有 PowerShell 脚本和模块捆绑到一个(可能有数千行)PS1 脚本中,并且一组集成测试均已通过。怎么办?

现在是时候开始修复变量引用、参数值、命令引用和其他阻止该解决方案在单个 PS1 脚本中工作的逻辑了。

不要试图立即开始完善这个解决方案。只需尝试让您的综合代码大杂烩完全像以前一样工作,尽管是通过单个脚本实现的。

您在此阶段的目标是让单个脚本的行为就像它被分成 1,000 个片段时一样。你需要一个统一的混乱起点。

继续进行更改并运行测试套件,直到所有测试通过。

困难的部分:重构 PowerShell 脚本

现在是时候开始重构代码了。这是有趣的部分!根据您的混乱脚本正在做什么,很大程度上取决于您的重构方式,但在下面您会找到一些建议。

为各种任务构建结构

每个大型脚本都有其内部执行的各种“任务”。也许是获取 NIC 属性、构建虚拟机、创建邮箱、删除用户等等。脚本完成任务。现在,这些任务可能都合并在一起了。

创建一个新脚本并将这些任务拆分为单个 PowerShell 脚本或另一个干净脚本中的区域。

更好地分解这些任务将帮助您了解代码的进展,同时在代码重构方面取得一些进展。

构建配置数据

此时,您在一个 PS1 脚本中就拥有了一切。这不好。您需要开始稍微模块化该脚本。对于您的第一个任务,构建一个配置数据存储。什么是配置数据存储?它是存储可能随时间变化的配置项的通用术语。

这部分是您之前记录的那些参数发挥作用的地方。

也许您有一个脚本,可以从 Windows 部署一直到配置操作系统来配置新服务器。在之前的mess脚本中,您发现了各种参数,例如ComputerNameIpAddressRunPatching等。这些参数改变了mess脚本的行为。您可能想保留它们。

在此阶段不必重新引入这些参数,而是创建一个配置数据存储来存储所有这些参数。例如,您可以在脚本所在的同一文件夹中创建一个名为 configuration.psd1 的文件。

configuration.psd1 文件可能如下所示:

@{
	'DefaultServerConfig' = @{
                'ServerName'         = 'SRV1'
		'ComputerNamePrefix' = 'xxxx'
		'IpAddress'          = $null
		'RunPatching'        = $true
	}
}

然后,在混乱脚本内部,您可以添加一个名为 Get-Configuration 的函数,该函数读取配置并将其分配给变量。

function Get-Configuration {
	param()
	Import-PowerShellDataFile -Path "$PSScriptRoot\configuration.psd1"
}

$configuration = Get-Configuration

现在,将其他脚本中的所有参数引用替换为对解决方案配置的引用,如下所示。

$Param1 = $configuration.ServerName

## code here

#endregion

$Param2 = $configuration.IpAddress

## code here

#endregion

您不必使用 PowerShell 数据文件。您可以使用 JSON、YAML 或 XML(如果您喜欢痛苦的话)。

您现在正在从逻辑代码中分离出配置。

消除裁员

议程上的下一个项目是裁员。冗余可以通过多种方式渗透到代码中。最常见的方法之一是忽略使用循环。

例如,如果您看到一些如下所示的行:

Do-Thing -ComputerName XXX -Thing yyyy
Do-Thing -ComputerName rrr -Thing hhhh

无需调用命令两次,而是使用参数展开和 foreach 循环来简化它。

$paramSets = @(
	@{
		ComputerName = 'XXX'
		Thing        = 'yyyy'
	}
	@{
		ComputerName = 'rrr'
		Thing        = 'hhhh'
	}
}

foreach ($set in $paramSets) {
	Do-Thing @set
}

上面的代码片段只是一个单一场景。有无数种方法可以删除冗余代码。添加循环可能是减少代码长度和简化事情的最简单方法之一。

模块化!

将庞大的混乱脚本分成小块,所有小块一起工作并构建一组标准的模块或功能,它们都可以共享。

  • 创建一个或多个 PowerShell 模块并将脚本转换为使用其中的函数。
  • 为所有任务创建小型、单一用途的函数。

你如何知道你什么时候完成

你怎么知道你什么时候完成了?如果您对所有这些问题的回答都是“是”,那么您就完成了。拍拍自己的背。

  1. 您对新的解决方案满意吗?
  2. 您是否遵循了本文中的所有提示?
  3. 在新的测试环境中运行旧解决方案和新解决方案后,您之前构建的所有集成测试是否都通过了?

如果您对所有这些问题的回答都是“是”,那么恭喜您,您已经完成了!

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯