Почему PSCustomObject заполняется отдельными символами вместо одной строки? - PullRequest
1 голос
/ 21 марта 2020

Я пытаюсь изучить PowerShell, переводя старый пакетный скрипт, который я создал для конвертации видео с помощью FFmpeg.

Я полагаю, что это почти не связано с рассматриваемой проблемой.

Этот фрагмент кода вызывает у меня проблемы:

[string]$FileList              = (Get-Clipboard).Split("`n")
[int]$Counter               = 0

$List                  = @(ForEach ($i in $FileList)
{
    [PSCustomObject]
   @{
        VideoHeight = (ffprobe.exe -v error -select_streams v:0 -show_entries stream=height -of csv=s=x:p=0 "$i")
        VideoDuration = (ffprobe.exe -v error -select_streams v:0 -show_entries stream=duration -of csv=s=x:p=0 "$i")
    }
    $Counter++
})

Хранить в $FileList список файлов из буфера обмена, разделенных новой строкой.

Сохранить в $Counter целое число 0.

Для каждого элемента в $FileList создайте новый объект, содержащий Высота и длительность предмета и добавьте 1 к $Counter.

Кажется прямым, верно? Вот подвох: если в $FileList есть только один файл с высотой 1080, $List.VideoHeight[0] вернет только 1, но если в $FileList будет хотя бы два файла, $List.VideoHeight[0] вернет 1080 .

Вывод командной строки:

Single File
$List.VideoHeight:
1080
$List.VideoHeight[0]:
1
Multiple Files
$List.VideoHeight:
1080
1080
720
$List.VideoHeight[0]:
1080

Есть идеи, что здесь происходит? Я застрял.

1 Ответ

3 голосов
/ 21 марта 2020

Используйте $List[0].VideoHeight, а не $List.VideoHeight[0].

В конце концов, с концептуальной точки зрения вы хотите получить значение .VideoHeight первого элемента списка ($List[0]), а не первого элемент значений высоты видео всего списка ($List.VideoHeight). [1]


Причина, по которой он работает с несколькими элементами в $List является то, что перечисление члена PowerShell затем возвращает массив из .VideoHeight значений свойств, где индексация с [0] работает, как ожидалось.

Причина, по которой это не так работа с ожидаемым элементом single в $List заключается в том, что тогда возвращается только значение свойства scalar (single) .VideoHeight и скаляр имеет тип [string]. Индексирование в одну строку возвращает отдельных символов в строке, что вы и видели.

Простая демонстрация:

PS> ([pscustomobject] @{ VideoHeight = '1080' }).VideoHeight[0]
1  # [char] '1', the first character in string '1080'

против.

PS> ([pscustomobject] @{ VideoHeight = '1080' }, 
     [pscustomobject] @{ VideoHeight = '1081' }).VideoHeight[0]
1080  # [string] '1080', the first element in array '1080', '1081'

Таким образом, есть два фактора, способствующих неожиданному поведению:

  • Перечисление членов PowerShell применяет тот же лог c, что и при сборе вывода конвейера: Если в коллекции, члены которой перечисляются, случается только один элемент , это значение элемента (свойства) возвращается как есть ; только 2 или более элементов приводят к [object[]] массиву значений.

    • К сожалению, перечисление членов ведет себя так; поведение может быть еще более удивительным, когда значения свойств сами являются массивами: результирующие массивы затем объединяются вместо того, чтобы превращаться в отдельные подмассивы в выходном массиве - см. эту проблему GitHub .
  • Стандартное поведение типа. NET System.String ([string]), позволяющее напрямую индексировать символы, составляющие строку (например, "foo"[0] yielding [char] 'f').

    • Строго говоря, это тот язык, который использует тип, который реализует синтаксический индексатор [...], который является формой syntacti c сахар; базовый тип имеет только параметризованное свойство с именем Chars, которое как C#, так и PowerShell более удобно предоставляют через [...].

    • Однако в случае [string] это, к сожалению, противоречит унифицированной обработке PowerShell сборов и скаляров, где обычно $scalar[0] совпадает с $scalar - см. этот ответ для справочной информации.


[1] Если собирая значения всех $List .VideoHeight значений свойств последовательно возвращал массив (почему это не объясняется во втором разделе), эти два оператора будут функционально эквивалентными хотя $List[0].VideoHeight все еще предпочтительнее для эффективности (не следует создавать промежуточный массив значений свойств), а также во избежание потенциальных конфликтов имен членов, которые по своей природе перечисления членов влечет за собой - см. этот выпуск GitHub .

...