9

这是令人难以置信的事情。这是test.ps1文件中的 PowerShell 代码片段:

Set-StrictMode -Version 2
mkdir c:\tmp\1  # same with 'md c:\tmp\1'

开始cmd.exe,使用脚本导航到文件夹test.ps1并运行它:

c:\tmp>powershell ".\test.ps1"

这会产生以下错误:

The variable '$_' cannot be retrieved because it has not been set.
At line:50 char:38
+         $steppablePipeline.Process($_ <<<< )
    + CategoryInfo          : InvalidOperation: (_:Token) [], ParentContainsEr
   rorRecordException
    + FullyQualifiedErrorId : VariableIsUndefined

为什么?

它在从 PowerShell 控制台启动但不是 cmd.exe 时有效。我在更大的脚本中发现了这个错误。这是一个WTF的时刻。

这个简单的脚本有什么问题?

4

2 回答 2

14

尽管已经找到了解决方法,但我认为人们可能会对解释感兴趣。

至于为什么它在 shell 与 cmd.exe 中的行为不同,请参阅Powershell 2.0 - Running scripts for the command line call vs. from the ISE

如参考中所述,以下两个命令之间存在差异:

powershell ".\test.ps1"
powershell -File ".\test.ps1"

使用第一种语法时,它似乎与范围混淆,导致 Set-StrictMode 命令修改在全局范围内定义的函数的严格模式。

这会在 mkdir 函数的定义中触发错误(或者可能是不正确的假设)。

该函数使用 GetSteppablePipeline 方法来代理 New-Item cmdlet 的管道。然而,作者忽略了一个事实,即即使管道中没有任何内容,PROCESS 部分仍会执行。因此,当到达 PROCESS 部分时,没有定义 $_ 自动变量。如果启用严格模式,则会发生异常。

Microsoft 解决此问题的一种方法是替换以下行:

    $steppablePipeline.Process($_)

具有以下内容:

    if (test-path Variable:Local:_) {
        $steppablePipeline.Process($_)
    }

我承认这可能不是修复它的最佳方法,但开销可以忽略不计。另一种选择是在 BEGIN 部​​分以某种方式测试管道是否为空,然后将 $_ 设置为 $null。

无论哪种方式,如果您使用“powershell.exe -File 文件名”语法运行脚本,则无需担心。

于 2011-07-01T20:21:17.277 回答
2

它看起来像一个错误(在 PowerShell 中)。

于 2011-02-16T23:26:49.397 回答