Windows PowerShell: значение переменной по умолчанию

Jan 03, 2022 02:15

Операционная система: «Windows 10 Pro».
Программа «Windows PowerShell», версия 5.1.

Очередной кусочек кода из книжки для начинающих работать с программой «PowerShell»:

while ($i -lt 5) {
$i += 1
if ($i -eq 3) {
continue
}
Write-Output $i
}

По утверждению автора учебника этот скрипт должен выдать такой результат:

1
2
4
5

И, действительно, если данный скрипт запустить сразу после открытия новой сессии работы с программой «Windows PowerShell», то ожидаемый результат будет получен. Однако, если тут же, сразу после первого запуска данного скрипта, запустить его во второй раз, то никакого результата программа не выдаст.

Почему скрипт срабатывает правильно сразу после открытия новой сессии работы?

Любой человек, изучавший строгие языки программирования, вроде Си или C++, довольно быстро может заметить, что в первой строке разбираемого скрипта используется необъявленная переменная $i. Эта переменная не только не объявлена ранее, ей еще не присвоено и первоначального значения.

В этом месте компилятор C++ уже забил бы тревогу, а в программе «Windows PowerShell» данный скрипт выполняется успешно, формальных ошибок с точки зрения этой программы в нем нет.

Дело в том, что в скриптах для программы «Windows PowerShell» переменные можно использовать без предварительного объявления. По умолчанию их значением считается значение $null. Таким образом, сравнение в первой строке скрипта (перед первой итерацией цикла while):

$i -lt 5 # результат: True
равно следующему:

$null -lt 5 # результат: True
Напомню, оператор -lt означает «меньше, чем». Перед первой итерацией цикла while это сравнение возвращает результат True (истина). Это очевидно, иначе скрипт не вернул бы вообще никаких результатов, так как цикл не запустился бы.

В одном из предыдущих постов я писал, что при сравнении значений различного типа с помощью оператора -lt правый операнд приводится перед сравнением к типу левого операнда. Так вот, для этого правила есть исключение, которое касается сравнения со значением $null в качестве левого операнда.

Проверим это. Если бы в данном случае правый операнд привелся бы к типу значения левого операнда, то у нас получилось бы следующее сравнение:

$null -lt $null # результат: False
Однако, как мы видим, это сравнение выдает результат False (ложь). Следовательно, в нашем сравнении $null -lt 5 правый операнд не приводится к типу значения $null.

В спецификации скриптового языка программы «Windows PowerShell» я не смог найти правила для этого случая. Поэтому буду исходить из предположений. Наиболее логично выглядит возможность, при которой в рассматриваемом случае левый операнд приводился бы к числовому типу (чтобы после этого сравнить его с правым операндом, представляющим число 5). При приведении значения $null к числовому типу оно трансформируется в число 0. Таким образом, получается следующее сравнение:

0 -lt 5 # результат: True
На первый взгляд, это нам подходит.

Однако, давайте посмотрим, как это работает на других числах (для теста я взял не только целые числа, но и дробные):

$null -lt 7 # результат: True
$null -lt 6 # результат: True
$null -lt 5 # результат: True
$null -lt 4 # результат: True
$null -lt 3 # результат: True
$null -lt 2 # результат: True
$null -lt 1 # результат: True
$null -lt 0.5 # результат: True
$null -lt 0 # результат: True, внимание
$null -lt -0.5 # результат: False
$null -lt -1 # результат: False
$null -lt -2 # результат: False
Вроде бы, всё хорошо, но при сравнении с нулем выходит не то, что мы ожидаем: если бы там было сравнение 0 -lt 0, то результат должен был бы быть равен значению False.

Глубже лезть я не буду (я не готов пока рыть исходники программы «PowerShell», хоть они открыты и есть в интернете). Будем считать, что при применении оператора -lt, если операнд слева является значением $null, а операнд справа является числом, то значение $null считается за число 0 (за исключением сравнения $null -lt 0, которое, это надо запомнить, выдает результат True).

Итак, при первом запуске обсуждаемого скрипта сразу после начала новой сессии работы с программой «Windows PowerShell» необъявленная переменная $i считается за число 0 перед первой итерацией цикла while. В начале первой итерации цикла while в переменную $i записывается число 1 (в арифметических операциях значение $null считается за число 0). После этого значение $null исчезает из расчетов и скрипт выполняется так, как рассчитывал автор.

Почему скрипт не срабатывает второй раз?

Как я понимаю, в программе «Windows PowerShell» цикл не создает своей области видимости, как, к примеру, в языке C++. Таким образом, переменная $i (где бы она ни была определена фактически: перед циклом или внутри цикла) будет доступна при втором запуске обсуждаемого скрипта, сразу после первого запуска. После первого запуска значение этой переменной будет равно числу 5. Очевидно, что при таком значении этой переменной входное условие цикла $i -lt 5 выдаст значение False и цикл не запустится. Таким образом, скрипт не выдаст никакого видимого результата, хоть и отработает без ошибок.

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

Как это исправить?

По идее, достаточно добавить первоначальное определение переменной $i. Вот так (красным цветом выделено изменение по сравнению с оригинальным скриптом):

$i = 0
while ($i -lt 5) {
$i += 1
if ($i -eq 3) {
continue
}
Write-Output $i
}

Вывод. Код, похожий на приведенный в начале поста, я видел в различных руководствах множество раз (не только для рассматриваемого тут скриптового языка). Просто уму непостижимо, почему начинающих программистов так часто учат таким плохим приемам написания кода. Причем подобным грешат довольно опытные люди.

Инструмент, Образование, Программирование

Previous post Next post
Up