PowerShell mkdir别名+ Set-StrictMode -Version 2.奇怪的bug.为什么?

Rom*_*man 9 powershell alias mkdir

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

Set-StrictMode -Version 2
mkdir c:\tmp\1  # same with 'md c:\tmp\1'
Run Code Online (Sandbox Code Playgroud)

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

c:\tmp>powershell ".\test.ps1"
Run Code Online (Sandbox Code Playgroud)

这会产生以下错误:

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
Run Code Online (Sandbox Code Playgroud)

为什么?

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

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

Art*_*gus 14

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

至于为什么它在shell与cmd.exe中的行为不同,请参阅Powershell 2.0 - 从ISE运行命令行调用脚本

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

powershell ".\test.ps1"
powershell -File ".\test.ps1"
Run Code Online (Sandbox Code Playgroud)

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

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

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

微软解决这个问题的一种方法是更换以下行:

    $steppablePipeline.Process($_)
Run Code Online (Sandbox Code Playgroud)

以下内容:

    if (test-path Variable:Local:_) {
        $steppablePipeline.Process($_)
    }
Run Code Online (Sandbox Code Playgroud)

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

无论哪种方式,如果您使用"powershell.exe -File filename"语法运行脚本,那么您不必担心它.