Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 354
Roman Avatar asked Feb 16 '11 20:02

Roman


2 Answers

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.

like image 61
Artomegus Avatar answered Nov 04 '22 08:11

Artomegus


It looks like a bug (in PowerShell).

like image 27
x0n Avatar answered Nov 04 '22 09:11

x0n