Powershell выводит только первый символ

У меня есть следующий набор данных:

       id|selectedquery|
 1|SELECT fieldX FROM tableA|
 2|SELECT fieldY FROM tableB|

этот набор данных используется в следующем коде

      $rows=($dataSet.Tables | Select-Object -Expand Rows)
$i=0
foreach ($row in $rows)
{
#Write-Output $rows.selectquery[$i].length
$query =   $rows.selectquery[$i]
#Write-Output $rows.selectquery[$i]
--doing some stuff--
$i++
}

Часто дает мне только первый символ значения в поле selectedquery будучи буквой «S».

Когда я удаляю [$i] из $rows.selectqueryон возвращает мне (по понятным причинам) несколько записей. Если я верну [$i] после $rows.selectquery[$i] все шло хорошо.

Кто-нибудь может объяснить такое поведение?

2 ответа

Вы захотите сослаться на SelectQuery собственность на любом $row или же $rows[$i]- не весь $rows коллекция:

      $row.SelectQuery
# or
$rows[$i].SelectQuery

Полезный ответ Матиаса показывает, как лучше всего решить вашу конкретную проблему.

Что касается того, что произошло:

Вы - непреднамеренно - использовали функцию перечисления членов PowerShell, когда использовали $rows.selectquery; то есть, хотя $rowsэто коллекция , которая сама по себе не имеет .selectqueryсвойство, PowerShell обращалась к этому свойству для каждого элемента коллекции и возвращала результирующие значения в виде массива .

Ловушка является то , что если коллекция имеет только один элемент, то возвращаемое значение не массив - это только один и только значение свойства элемента сам по себе .

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

  • Другими словами, $collection.SomeProperty эквивалентно $collection | ForEach-Object { $_.SomeProperty }а не , что было бы больше смысла, потому что он всегда возвращает массив (коллекцию), $collection.ForEach('SomeProperty')
  • В выпуске GitHub № 6802 обсуждается эта проблема.

Хотя такое поведение часто не вызывает проблем, поскольку PowerShell предлагает унифицированную обработку скаляров и коллекций (например, (42)[0], такой же как 42сам; см. этот ответ ), проблема возникает, если возвращаемое единственное значение оказывается строкой , потому что при индексации строки возвращаются ее символы .

  • Обходной путь : приведите к перед применением индекса:
    • ([array] $rows.selectquery)[0]

Простой пример :

      # Multi-element array.
[array] $rows1 = [pscustomobject] @{ selectquery = 'foo' },
                 [pscustomobject] @{ selectquery = 'bar' }

# Single-element array:
[array] $rows2 = [pscustomobject] @{ selectquery = 'baz' }

# Contrast member enumeration + index access between the two:
[pscustomobject] @{
  MultiElement = $rows1.selectquery[0]
  SingleElement = $rows2.selectquery[0]
  SinglElementWithWorkaround = ([array] $rows2.selectquery)[0]
}

Вышеупомянутое дает следующее:

      MultiElement SingleElement SinglElementWithWorkaround
------------ ------------- --------------------------
foo                      b baz

Как видите, многоэлементный массив работал так, как ожидалось, потому что перечисление членов тоже вернуло массив, а одноэлементный массив привел к единственной строке 'baz' возвращаются и 'baz'[0]возвращает свой первый символ , 'b'.

Трансляция на [array] сначала избегает этой проблемы ( ([array] $rows2.selectquery)[0]).

  • С использованием @(...), оператор подвыражения массива - @($rows.selectquery)[0]- это еще один вариант, но для эффективности его следует использовать только в командах (например, @(Get-ChildItem -Name *.txt)[0]) а не выражения , как в данном случае.)
Другие вопросы по тегам