为什么 运行 通过模块清单的 ScriptsToProcess 指令的脚本取消定义 $LASTEXITCODE?

Why does running a script via a module manifest's ScriptsToProcess directive undefine $LASTEXITCODE?

我有以下使用模块清单文件中的 ScriptsToProcess 指令的 PowerShell 模块:

test-lstExtCode.psd1:

@{
   RootModule        = 'test-lstExtCod.psm1'
   ModuleVersion     = '0.1'

   FunctionsToExport = @(
     'get-importDate'
   )

   ScriptsToProcess  = @(
      'write-import-date.ps1'
   )
}

模块正文 (test-lstExtCod.psm1) 内容为:

set-strictMode -version 3

function get-importDate {
   get-content $psScriptRoot/import-date
}

write-import-date.ps1的文字是

set-strictMode -version 3

get-date > $psScriptRoot/import-date

导入此模块或使用 get-importDate 取消定义全局变量 $LASTEXITCODE:

PS> $LASTEXITCODE -eq $null
True
PS> get-importDate

Thursday, July 29, 2021 9:55:16 PM


PS> $LASTEXITCODE -eq $null
The variable '$LASTEXITCODE' cannot be retrieved because it has not been set.
At line:1 char:1
+ $LASTEXITCODE -eq $null
+ ~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (LASTEXITCODE:String) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : VariableIsUndefined

我不明白是什么原因导致 $LASTEXITCODE 未定义。

如果我注释 ScriptsToProcess 指令,此行为就会消失,并且 $LASTEXITCODE 在导入模块后定义。

tl;dr

  • 在您的场景中,$LASTEXITCODE 不是 undefined 由您的模块导入 - 它从未被定义开始。

  • 但是,您的模块导入激活了严格模式版本3 调用者范围(由于使用 ScriptsToProcess 模块清单条目),因此第二次尝试访问 $LASTEXITCODE 导致错误,因为严格模式版本 1 及更高版本仅允许访问 defined 变量(而默认情况下(严格模式为 off)尝试访问未定义变量的计算结果为 $null 而不是导致错误)。


注:

  • 在原始 PowerShell 会话中(从 -NoProfile 开始),automatic $LASTEXITCODE variable 定义。

  • 仅当在会话中(首先)调用外部程序时才会创建它,在这种情况下,它被设置为调用的 进程退出代码 .

因此,Set-StrictMode -Version 1 或更高版本有效,您应该只尝试访问 automatic $LASTEXITCODE variable 如果您知道至少调用了一个外部程序

  • Set-StrictMode 默认为 -Off,这意味着试图访问一个 undefined 变量悄悄地评估为 $null,这就是为什么 $LASTEXITCODE -eq $null[1] - 在没有调用 Set-StrictMode 的情况下 - 最初为你评估为 $true

  • 虽然模块的代码通常 运行 在它自己的范围域(也称为会话状态)中,但脚本 运行 通过 ScriptsToProcess 模块清单条目 运行 在 调用者的 范围内,因此可以影响其状态。因此,在导入您的模块后,严格模式版本 3 在您的(非模块)代码中生效,并且尝试访问 $LASTEXITCODE 失败,因为尚未调用任何外部程序并且 $LASTEXITCODE 因此尚未定义。

    • 一个推论是您应该避免在 ScriptsToProcess 脚本中包含 全局 改变调用者状态的命令。

一般来说,$LASTEXITCODE 值一旦设置,通常会持续到同一会话中的下一个外部程序调用,建议在给定后尽快检查 $LASTEXITCODE外部程序调用,为了概念清晰。


[1] 要可靠地测试 $null,请将其放在 -eq / [=35= 的 LHS 上] 手术;例如,$null -eq $var。如果你将 $null 放在 RHS - $var -eq $null - 而 $var 恰好是一个 集合 (例如数组), return值为匹配元素数组,即$var元素的数组,其值为$null ,这是一个不同的操作 - 请参阅 about_Comparison_Operators