Псевдоним PowerShell mkdir + Set-StrictMode -Version 2. Странная ошибка. Почему?

Это что-то невероятное. Это фрагмент кода PowerShell в файле test.ps1:

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-момент.

Что не так с этим простым скриптом?


person Roman    schedule 16.02.2011    source источник


Ответы (2)


Несмотря на то, что обходной путь уже найден, я подумал, что людей может заинтересовать объяснение.

О том, почему он ведет себя по-разному в оболочке по сравнению с cmd.exe, см. call-vs-from-the-ise">Powershell 2.0 — запуск скриптов для вызова из командной строки по сравнению с ISE

Как упоминалось в справочнике, между следующими двумя командами есть разница:

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

При использовании первого синтаксиса кажется, что он нарушает область действия, заставляя команду Set-StrictMode изменять строгий режим для функций, определенных в глобальной области.

Это вызывает ошибку (или, возможно, неправильное предположение) в определении функции mkdir.

Функция использует метод GetSteppablePipeline для передачи конвейера командлету New-Item. Однако автор не учел тот факт, что секция PROCESS все еще выполняется, даже если в конвейере ничего нет. Таким образом, при достижении раздела PROCESS автоматическая переменная $_ не определяется. Если включен строгий режим, произойдет исключение.

Один из способов для Microsoft учесть это — заменить следующую строку:

    $steppablePipeline.Process($_)

со следующим:

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

Я признаю, что это может быть не лучший способ исправить это, но накладные расходы будут незначительными. Другим вариантом было бы как-то проверить, пуст ли конвейер в разделе BEGIN, а затем установить $_ в $null.

В любом случае, если вы запускаете свои сценарии с синтаксисом «powershell.exe -File filename», вам не нужно об этом беспокоиться.

person Artomegus    schedule 01.07.2011

Похоже на ошибку (в PowerShell).

person x0n    schedule 16.02.2011
comment
Я использовал New-Item 'blah' -type directory в качестве обходного пути. Проблема только с псевдонимами mkdir и md - person Roman; 25.02.2011
comment
Я собираюсь принять этот ответ, но с примечанием избегайте использования псевдонимов в сценариях PowerShell, и у вас не будет таких странных проблем. - person Roman; 31.03.2011
comment
Но это не про псевдонимы. mkdir — это функция, и у нее есть проблема, когда включен строгий режим. - person Roman Kuzmin; 02.07.2011