为什么 运行 通过模块清单的 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。
我有以下使用模块清单文件中的 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。