2011-02-16 105 views
9

這是令人難以置信的。這是test.ps1文件中的PowerShell代碼片段:PowerShell mkdir別名+ Set-StrictMode -Version 2.奇怪的錯誤。爲什麼?

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時刻。

這個簡單的腳本有什麼問題?

回答

14

儘管已經找到解決方法,但我認爲人們可能會對解釋感興趣。

至於爲什麼它在外殼與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部分時,$ _自動變量未定義。如果啓用了嚴格模式,則會發生異常。

微軟考慮到這將是替換以下行的一種方式:

$steppablePipeline.Process($_) 

下列要求:

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

我承認這可能不是解決它的最好辦法,但開銷可以忽略不計。另一個選擇是以某種方式測試BEGIN節中的管道是否爲空,然後將$ _設置爲$ null。

無論採用哪種方式,如果您使用「powershell.exe -File文件名」語法運行腳本,則無需擔心。

2

它看起來像一個錯誤(在PowerShell中)。

+3

我使用了`New-Item'blah'-type directory`作爲解決方法。問題似乎只有`mkdir`和`md`別名 – Roman 2011-02-25 09:53:29

+0

我打算接受這個答案,但註釋「避免在PowerShell腳本中使用別名,並且不會出現這樣的奇怪問題」。 – Roman 2011-03-31 18:04:44