PowerShell mkdir alias + Set-StrictMode -Version 2. Странная ошибка.Зачем? - PullRequest
9 голосов
/ 16 февраля 2011

Это что-то невероятное. Это фрагмент кода 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.

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

Ответы [ 2 ]

14 голосов
/ 02 июля 2011

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

Относительно того, почему он ведет себя по-разному в оболочке по сравнению с cmd.exe, см. 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, вам не нужно об этом беспокоиться.

2 голосов
/ 17 февраля 2011

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

...