Добавление элемента в массив в блоке сценариев powershell преобразует массив в строку

Я заметил странное поведение при использовании массивов в блоках сценариев. Следующий код показывает проблему:

$array = @("x", "y")

Write-Host "$($array.GetType().Name)"
Write-Host "$($array.GetType().BaseType)"

$bad = {
    $array += "z"
    Write-Host "$($array.GetType().Name)"
    Write-Host "$($array.GetType().BaseType)"
    $array
}

$good = {
    $array = $array.Clone()
    $array += "z"
    Write-Host "$($array.GetType().Name)"
    Write-Host "$($array.GetType().BaseType)"
    $array
}

& $good
& $bad

Выполнение скрипта приведет к следующему выводу:

Object[]
array
Object[]
array
x
y
z
String
System.Object
z

Скриптблок $bad работает не так, как я ожидал. Он преобразует массив в строку, но должен просто добавить элемент z в массив. Если элемент не добавлен, массив можно использовать, как ожидалось.

Я заметил такое поведение в powershell 5.0 и 5.1, но не в ISE. Это баг или кто может объяснить?


person Tobias Wollgam    schedule 15.06.2018    source источник


Ответы (2)


Это вопрос масштаба. Переменная в левой части операции присваивания в блоках сценариев определяется в локальной области видимости.

Это утверждение

$array = $array.Clone()

клонирует значение глобальной переменной $array и присваивает его локальной переменной $array (то же имя, но другая переменная из-за другой области действия). Затем локальная переменная $array содержит копию исходного массива, поэтому следующий оператор

$array += "z"

добавляет новый элемент в этот массив.

В другом блоке сценария вы сразу же добавляете строку к (локальной) переменной $array. В этом контексте локальная переменная пуста, поэтому $array += "z" имеет тот же эффект, что и $array = "z", оставляя вам переменную, содержащую только строку «z».

Укажите правильную область, и вы получите ожидаемое поведение:

$array = @("x", "y")

$not_bad = {
    $script:array += "z"
    Write-Host "$($script:array.GetType().Name)"
    Write-Host "$($script:array.GetType().BaseType)"
    $script:array
}

& $not_bad

Помните, однако, что это фактически изменит исходный массив в глобальной области/области сценария (ваш пример $good оставляет исходный массив без изменений).

Я не уверен, что считаю такое поведение ошибкой, но это определенно ошибка.

person Ansgar Wiechers    schedule 15.06.2018
comment
Спасибо. Хотя ваш ответ правильный, я понимаю, что пример сценария может не архивировать всю проблему. Исходный сценарий ведет себя по-разному в консоли и ISE. Пример скрипта ведет себя одинаково. - person Tobias Wollgam; 15.06.2018
comment
ISE и консоль — это разные хост-процессы, которые, как известно, в некоторых отношениях ведут себя по-разному. Я бы предложил опубликовать новый вопрос с обновленным кодом и описанием проблемы, если эта проблема не устранена. - person Ansgar Wiechers; 15.06.2018

Я хотел бы опубликовать свое предпочтительное решение, основанное на объяснении Ансгара:

$array = @("x", "y")

$not_bad = {
    $array = $array + "z"
    Write-Host "$($array.GetType().Name)"
    Write-Host "$($array.GetType().BaseType)"
    $array
}

& $not_bad

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

$array = $array

подойдет, но эта строка может сбивать с толку.

person Tobias Wollgam    schedule 15.06.2018
comment
Трудно поверить, что разное поведение $a = $a + "z" и $a += "z" было преднамеренным, а не ошибкой. - person Tobias Wollgam; 15.06.2018
comment
$a += "z" — это не просто синтаксический сахар для $a = $a + "z". Его семантика на самом деле немного отличается. Последнее утверждение означает добавление «z» к переменной с правой стороны и присвоение результата переменной с левой стороны, тогда как первое означает добавление «z» к переменной слева. - стороны рук. Они дают одинаковый результат, когда вы используете одну и ту же переменную с обеих сторон присваивания. Однако в вашем конкретном сценарии это не так ($a слева находится в локальной области, $a справа находится в глобальной области). - person Ansgar Wiechers; 16.06.2018
comment
Вы совершенно правы. Но это затрудняет понимание (и написание) кода. В c++ можно запрограммировать различное поведение для обоих операторов + и +=, но не следует этого делать, потому что это сбивает с толку. В PS это по замыслу!? - person Tobias Wollgam; 18.06.2018
comment
Проблема заключается не в операторах как таковых, а в различной оценке областей действия по обе стороны задания. Как я уже сказал, это определенно подвох. Однако я не знаю причин решения заставить PowerShell (или базовую платформу .Net) оценивать вещи таким образом. Может быть, @BrucePayette сможет пролить свет. - person Ansgar Wiechers; 18.06.2018