PowerShell教程:4 bug!(解决常见脚本问题)

本章涵盖:

  • 调试简介
  • 导航 PowerShell 和 VS Code 提供的调试工具
  • 使用消息和标记识别和更正语法错误
  • 使用中断和写入主机 cmdlet 查找和更正逻辑错误

现在你的技巧包里已经有一些脚本,学习一项新技能,调试对你来说很重要。事实上,编写代码是容易的部分。调试是困难的部分,弄清楚为什么你会得到结果 A 而不是 B。

即使你没有真正想写原创剧本,这一章也很重要!因为在一个域到下一个域上存在许多环境差异。Tiny PowerShell Project 手册中提供的脚本旨在在大多数环境中开箱即用。但是,您仍然可能会遇到实现它们的问题,本章将为您提供所需的工具,使您能够解决实现问题,并在您自己的环境中看到这些 Tiny PowerShell 项目的价值。

4.1 理解红色

与许多程序不同,人们可能会在Windows平台上运行,这些程序会为您提供模糊的错误消息,这些错误消息几乎没有什么可处理的,例如“发生了错误”。PowerShell提供了有用的示例,说明问题是什么以及如何解决它。

PowerShell 中的错误显示为红色,看着充满红色的屏幕而不知道它试图告诉您什么可能会令人生畏。让我们看一下尝试在强化的PC上使用保存的脚本时可能会看到的常见错误(图4.1)。

图4.1 查看错误信息

powershell的使用方法,powershell死机代码

图 4.1 中的错误消息中提到的执行策略是一项安全功能,它详细说明了 PowerShell 可以在系统上执行脚本的条件。通过实施执行策略,您可以防止所有脚本或未签名脚本在您的 PC 上运行,从而帮助防止恶意脚本的执行。

但是,在我们的例子中,我们知道这不是恶意脚本,因此我们可以删除此限制。我们利用 Set-ExecutionPolicy cmdlet 来更改系统上的执行策略。

Set-ExecutionPolicy -ExecutionPolicy Unrestricted

这将取消限制您的计算机运行未签名的脚本。可以通过再次运行设置执行策略 cmdlet 来重新启用此安全功能。

Set-ExecutionPolicy -ExecutionPolicy Restricted

运行上述命令将在您的 PC 上重新启用执行策略限制。

图 4.2 使用错误消息中的信息查找并解决问题

powershell的使用方法,powershell死机代码

图 4.1 和 4.2 中探讨的错误消息可帮助我们在开发脚本时更正错误并了解有关 PowerShell 的更多信息。接下来,让我们看看如何利用VSCode和PowerShell错误消息进一步消除PowerShell中的脚本错误,以便从损坏到工作。

4.2 项目代码

警告:

与本书中的所有其他代码不同,此代码是故意中断的。总的来说,它看起来应该非常像我们迄今为止和本书其余部分一直在使用的其他一些脚本。但是当你运行它时,正如所写的那样,它会有几个语法错误,甚至一个逻辑错误。

如果您无法理解代码在做什么,请不要担心。我们将在本书后面介绍循环和 if 语句。目前,我们只是尝试调试代码以使其运行。

如果你熟悉墙上的歌曲《100瓶啤酒》,你应该从这个脚本中得到启发(图4.1)。当我们完成代码调试并最终让它运行时,它会将整首歌曲的完整歌词打印到屏幕上;当然,它现在还不会这样做。

这个 Tiny PowerShell 项目中的“项目”不是关于脚本将为我们做什么;而是如何使您(读者)成为更有能力的脚本编写者,并能够在看到常见问题时对其进行调试。

图 4.3 原始微型 PowerShell 项目代码

powershell的使用方法,powershell死机代码

当我们运行此代码时,VS Code 会尽最大努力帮助我们识别语法问题,以帮助我们运行代码。

在 PowerShell 5.x 与 PowerShell 7.x 中进行调试

PowerShell 5.1可以在VS Code中运行;但是,它也有一个内置的Windows PowerShell ISE构建在.Net Framework上。故障排除技术类似于我们在PowerShell 7.x中使用的技术,但VS Code使这变得更加容易。由于与版本 5.1 不同,PowerShell 7.x 不在 PowerShell ISE 上运行,因此本书的所有错误检测和修复都将使用 VS Code 完成。

4.2.1 提示和技巧

在调试代码时,实际上有两种类型的错误,语法问题和逻辑问题。语法问题是解释器不理解我们的代码并且无法运行它。这些类型的错误曾经是一场噩梦,程序员可能会花几天时间寻找一个应该是分号的冒号。然而,对于当今的现代脚本编译器和解释器,大多数语法问题通常很容易找到(图 4.4)。

图 4.4 查找 VS Code 的语法问题

powershell的使用方法,powershell死机代码

4.3 语法问题

VS Code 已确定代码语法的问题。开始调试的好地方是单击“问题选项卡”并快速找到问题区域(图 4.5)。

图 4.5 似乎没有意义的语法问题示例。

powershell的使用方法,powershell死机代码

查看“问题”选项卡的输出,我们看到PowerShell通知我们代码中的某处缺少大括号。但是这段代码显然存在问题,因为我们显然包含右大括号。由于某种原因,VS Code 没有正确看到它。我们可以逐步解决 VS Code 检测到的问题,看看是否有任何内容跳出来(图 4.6)。

图 4.6 解决 VS Code 的常见语法问题。

powershell的使用方法,powershell死机代码

这是我们的第一个收获。VS Code 告诉我们,在第 14 行缺少一个结束终止符 (“)。但是第 14 行末尾的终止符 应该是 我们在第 11 行开始的字符串的结束终止符,而不是开始的终止符。

看看第11行和第14行之间,我们可以看到罪魁祸首。第 11 行末尾有一个额外的引号。VS Code 将$song变量视为:

$song="`t$i $object of beer on the wall.`n"

第 12-14 行不再被视为$song变量的一部分。第 14 行末尾的引用被视为新刺痛的 开始 。因此,超出此点的所有内容都被视为字符串。这就是它没有在第 18 行看到关闭大括号的原因。

我们可以更正代码,使其看起来像这样:

$i="100"
do{
    $object="bottles"
    $objec2="bottles"
        If($i == 1){
            $object="bottle"
        }
        If ($i2 -eq 1){
            $object2="bottle"
         }
    $song="`t$i $object of beer on the wall.`n
        $i $object of beer.`n
        Take one down pass it around.`n
        $i2 $object2 of beer on the wall.`n`n"
    $i2=$i-1
    Write-Host $song
    $i--
} until ($i -lt 1)

当我们运行此版本的代码时,语法问题明显减少(图 4.7)。但现在我们陷入了一个无限循环!我们可以通过点击 VS Code 上的红色方块(停止)按钮来停止执行。

图 4.7 解决最终语法问题。

powershell的使用方法,powershell死机代码

剩下的语法问题告诉我们 objec2 被分配但从未使用过。我们在代码中多次使用 $object 2。但是当我们在第 4 行创建变量时,我们似乎忘记了对象中的“t”,并意外地将变量创建为 objec2 而不是 object2。这是一个简单的修复。

$i="100"
    do{
        $object="bottles"
        $object2="bottles"
            If($i == 1){
                $object="bottle"
            }
            If ($i2 -eq 1){
                $object2="bottle"
             }
    $song="`t$i $object of beer on the wall.`n
        $i $object of beer.`n
        Take one down pass it around.`n
        $i2 $object2 of beer on the wall.`n`n"
    $i2=$i-1
    Write-Host $song
    $i--
} until ($i -lt 1)

修复代码后,我们不再看到任何 VS Code 标识的问题,也不会在窗口中看到任何带下划线的红色。但是,代码仍然无法成功运行。当我们运行它时,我们看到两条错误消息:

  • 一个关于术语=不被识别。
  • 一个关于 -– 运算符只处理数字。

如果这些错误不是语法错误,它们是什么?这些是逻辑错误。

4.4 逻辑问题

现在代码中的剩余问题是逻辑问题。这些有点棘手。幸运的是,我们有一些工具可以帮助我们逐步诊断问题。

4.4.1 VSCode 断点

我们可以利用VSCode和调试窗口逐步尝试我们的代码。这将帮助我们检查我们是否按照我们期望的单步执行代码时设置了变量。希望我们能找出我们的逻辑错误并找出它们。

添加断点

断点是一种我们可以告诉 PowerShell 在代码到达脚本中的特定点时停止执行代码的方法。这是能够逐步完成代码的好方法,因为我们可以运行一个部分然后中断它,确保每个步骤都按照我们的期望去做。如果我们在代码中插入中断,我们将暂时处理迫使我们手动停止代码的无限循环。

我们还调用每个变量,以确保它们保持我们期望的值。

在休息之前,我们有变量:

$i

$object

$object 2

$i 2

让我们检查一下PowerShell对这些变量有什么。首先,我们将添加 Write-Host 语句来检查变量中的值。通常,当脚本失败时,这是因为变量中的值不是我们认为应该的值。验证这些变量的输入和输出可能是调试的关键第一步。

除此之外,我们还将利用 VS Code 插入一个断点,脚本的执行将暂停,直到我们手动继续它。这使我们能够逐步检查我们的脚本。图 4.8 和 4.9 详细介绍了添加断点。

$i="100"
do{
    $object="bottles"
    $object2="bottles"
        If($i == 1){
            $object="bottle"
        }
        If ($i2 -eq 1){
            $object2="bottle"         
        }
        Write-Host "variable i is: $i"
        Write-Host "variable object is: $object"
   Write-Host "variable object2 is: $object2"
   Write-Host "variable i2 is: $i2"
      $song="`t$i $object of beer on the wall.`n
        $i $object of beer.`n
        Take one down pass it around.`n
        $i2 $object2 of beer on the wall.`n`n"
    $i2=$i-1
    Write-Host $song
    $i--
} until ($i -lt 1)

图 4.8 使用 VSCode 调试器创建断点。

powershell的使用方法,powershell死机代码

图 4.9 添加 VSCode 断点

powershell的使用方法,powershell死机代码

图 4.10 使用 VS 代码断点

powershell的使用方法,powershell死机代码

幻像变量

根据运行此代码的时间,您可以看到非常奇怪的行为。有时,当您运行此代码时,您可能会看到返回的值 99 表示 $i 2。其他时候,当您运行它时,您可能看不到变量 $i 2 的值(图 4.11)。

图 4.11 会话中未清除变量的示例。

powershell的使用方法,powershell死机代码

这是因为,当您分配变量时,该变量存储在首次调用它的会话中。如果您在我们放入断点之前执行了代码,PowerShell 将遍历代码以将值分配给 $ i2 。因此,当我们询问 PowerShell $i 2 的值是什么时,它会查看变量是否已定义,如果是,则返回值。

但是,如果我们重新启动 VS Code(并启动新的 PowerShell 会话),则中断会在 PowerShell 将值分配给 $i 2 之前停止代码,并且它没有分配任何值,因此它不返回任何内容或 NULL 。

调试时,最好始终清除变量值,这样就不会显示不希望显示的旧值。

我们可以解决我们看到的问题 $i 2 的空值 ,只需将行 :“$i 2=$i-1”移动到我们休息点上方的某个位置。因此,让我们将其移至我们首次使用的 $i 2 部分的正上方。

$i="100"
do{
    $object="bottles"
    $object2="bottles"
    $i2=$i-1
 
        If($i == 1){
            $object="bottle"
        }
        If ($i2 -eq 1){
            $object2="bottle"
         }
        Write-Host "variable i is: $i"
        Write-Host "variable object is: $object"
   Write-Host "variable object2 is: $object2"
   Write-Host "variable i2 is: $i2" 
    $song="`t$i $object of beer on the wall.`n
        $i $object of beer.`n
        Take one down pass it around.`n
        $i2 $object2 of beer on the wall.`n`n"
    Write-Host $song
    $i--
} until ($i -lt 1)

图 4.12 解决幻像变量问题。

powershell的使用方法,powershell死机代码

这些起始值现在看起来很棒(图4.12)。但是我们仍然在输出中看到一些错误。首先看这个(图4.13):

图 4.13 PowerShell 比较运算符的问题。

powershell的使用方法,powershell死机代码

错误告诉我们在第 6 行无法识别术语“=”。这是在我们的 if 语句中,我们正在检查 $i 的值是否等于 1。但是,如果您还记得第 3 章,PowerShell 与 C 等某些语言不同,它使用 -eq 来表示值是否等于,而不是我们在这里使用的“==”。让我们解决这个问题!

$i="100"
do{
    $object="bottles"
    $object2="bottles"
    $i2=$i-1
 
        If($i -eq 1){
            $object="bottle"
        }
        If ($i2 -eq 1){
            $object2="bottle"
         }
        Write-Host "variable i is: $i"
        Write-Host "variable object is: $object"
   Write-Host "variable object2 is: $object2"
   Write-Host "variable i2 is: $i2" 
    $song="`t$i $object of beer on the wall.`n
        $i $object of beer.`n
        Take one down pass it around.`n
        $i2 $object2 of beer on the wall.`n`n"
    Write-Host $song
    $i--
} until ($i -lt 1)

图 4.14 所有问题都解决了?

powershell的使用方法,powershell死机代码

VS Code 没有发现任何问题,起始变量看起来都像我们期望的那样(图 4.14)。

$i="100"
do{
    $object="bottles"
    $object2="bottles"
    $i2=$i-1
 
        If($i -eq 1){
            $object="bottle"
        }
        If ($i2 -eq 1){
            $object2="bottle"
         }
         Write-Host "variable i is: $i"
          Write-Host "variable object is: $object"
    Write-Host "variable object2 is: $object2"
    Write-Host "variable i2 is: $i2"
    $song="`t$i $object of beer on the wall.`n
        $i $object of beer.`n
        Take one down pass it around.`n
        $i2 $object2 of beer on the wall.`n`n"
    Write-Host $song
    $i--
} until ($i -lt 1)

单步执行断点

但是,当我们单步跨越断点时会发生什么?当我们使用 VSCode 的调试器时,我们可以手动设置断点。VSCode 还插入了自动断点,以帮助我们识别代码中的问题。我们可以使用 Step 工具单步执行这些断点,以查看代码是否执行(图 4.15)。

图 4.15 单步执行断点

powershell的使用方法,powershell死机代码

图4.16 最后!

powershell的使用方法,powershell死机代码

“--”运算符仅适用于数字(图 4.16)。$i-- 是 $i=$i-1 的简写。但是由于这是从变量中减去一个值并替换它,因此变量需要是一个数字。

编程速记

程序员一遍又一遍地遇到许多常见的技术。为了节省时间,精力和打字,为许多这些非常常见的事情创建了速记。以下是一些在此阶段对您有用的信息。

powershell的使用方法,powershell死机代码

PowerShell 将值视为 System.String 或 (String) [第 2 章]。就像你不能从单词中减去一个数字(例如,cat -1没有任何意义),你不能从字符串中减去一个整数。PowerShell没有将$i的值视为数字100,而是将单词“100”视为单词。

字符串是通过将它们放在引号中来定义的。我们可以从第 1 行中删除引号,PowerShell 现在应该将 $i 的值视为数字 100。

解决循环问题后,我们可以删除显示变量起始值的写入主机:i、i2、object 和 object2。

$i=100
do{
    $object="bottles"
    $object2="bottles"
    $i2=$i-1
 
        If($i -eq 1){
            $object="bottle"
        }
        If ($i2 -eq 1){
            $object2="bottle"
         }
    $song="`t$i $object of beer on the wall.`n
        $i $object of beer.`n
        Take one down pass it around.`n
        $i2 $object2 of beer on the wall.`n`n"
    Write-Host $song
    $i--
} until ($i -lt 1)

图4.17 成功!

powershell的使用方法,powershell死机代码

成功!没有语法或逻辑问题(图 4.17)。VS Code 没有发现任何问题,歌曲的所有一百行都已打印到我们的输出中。祝贺。如果您按照本章中的步骤操作,您将获得一些宝贵的经验和工具,您可以使用这些经验和工具来帮助使创建或从其他源找到的脚本正常工作。

4.5 小结

  • 当 PowerShell 显示错误时,它将经常为解决问题提供重要帮助。该错误可以通过谷歌搜索,输出通常会为您提供一个Microsoft知识链接,该链接将提供有关导致脚本终止的特定函数的详细信息。
  • 脚本可能包含语法错误和逻辑问题,这些问题可能导致脚本意外终止(崩溃)或根本无法生成预期的结果。
  • VS Code 将使用 红色波浪线 突出显示许多语法问题,以帮助你确定脚本中存在语法问题的位置。
  • 如果使用 Microsoft VS Code 作为 IDE,则可以从输出屏幕的“问题”选项卡访问集成的语法检查器。此检查器可以快速识别脚本中的语法问题。
  • 逻辑脚本问题是指脚本问题导致其无法正常运行,而不导致其意外终止。
  • 对逻辑错误进行故障排除时,可以利用 点逐个执行代码,一次单步执行一段,直到找到脚本行为与预期不同的点。
  • 可以使用写入主机 cmdlet 检查变量和输入的值,以确保该值与预期值匹配。
  • 大多数变量存储在初始化它们的 PowerShell 会话中,并且在脚本终止后通常驻留在会话中。调试时,请在使用变量之前清除变量的值,以确保变量的值按预期方式设置。