开发者

PowerShell mkdir alias + Set-StrictMode -Version 2. Strange bug. Why?

It's something unbelievable. This is a PowerShell code snippet in test.ps1 file:

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

Start cmd.exe, navigate to folder with test.ps1 script and run it:

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

This produces the following error:

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

Why?

It works when started from PowerShell console but not cmd.exe. I discovered this bug in much larger script. It was a WTF moment.

What is wrong with this simple script?


Even though a workaround has already been found, I thought people might be interested in an explanation.

As to why it behaves differently in the shell versus cmd.exe, see Powershell 2.0 - Running scripts for the command line call vs. from the ISE

As mentioned in the reference, there is a difference between the following two commands:

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

When using the first syntax, it seems to mess with scope, causing the Set-StrictMode command to modify the strict mode for functions defined at the global scope.

This triggers a bug (or an incorrect assumption, perhaps) in the definition of the mkdir function.

The function makes use of the GetSteppablePipeline method to proxy the pipeline for the New-Item cmdlet. However, the author neglected to account for the fact that the PROCESS section is still executed even when there is nothing in the pipeline. Thus, when the PROCESS section is reached, the $_ automatic variable is not defined. If strict mode is enabled, an exception will occur.

One way for Microsoft to account for this would be to replace following line:

    $steppablePipeline.Process($_)

with the following:

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

I admit that this may not be the best way to fix it, but the overhead would be negligible. Another option would be to somehow test if the pipeline is empty in the BEGIN section, and then set $_ to $null.

Either way, if you run your scripts with the "powershell.exe -File filename" syntax, then you won't need to worry about it.


It looks like a bug (in PowerShell).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜