PowerShell выводит элементы массива при интерполяции в двойных кавычках - PullRequest
11 голосов
/ 08 февраля 2012

Я обнаружил странное поведение в массивах PowerShell и двойных кавычках. Если я создаю и печатаю первый элемент в массиве, например:

$test = @('testing')
echo $test[0]

Output:
testing

Все отлично работает. Но если я поставлю двойные кавычки:

echo "$test[0]"

Output:
testing[0]

Была оценена только переменная $ test, а маркер массива [0] трактовался буквально как строка. Легкое решение - просто избегать интерполяции переменных массива в двойных кавычках или сначала назначать их другой переменной. Но так ли это по замыслу?

Ответы [ 3 ]

19 голосов
/ 08 февраля 2012

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

"$test[0]"

Он видит $ test в качестве следующей переменной, он понимает, что это массив, и у него нет хорошего способа отображения массива, поэтому он решает, что он не может интерполировать, и просто отображает строку в виде строки. Решение состоит в том, чтобы явно указать PowerShell, где начинается бит для интерполяции и где он останавливается:

"$($test[0])"

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

"{0}" -f $test[0]
9 голосов
/ 08 февраля 2012

В таких случаях вы должны сделать:

echo "$($test[0])"

Другая альтернатива - использовать форматирование строки

echo "this is {0}" -f $test[0]

Обратите внимание, что это будет иметь место при доступе к свойствам в строках. Как "$a.Foo" - должно быть записано как "$($a.Foo)"

3 голосов
/ 07 мая 2018

Полезный ответ EBGreen содержит эффективные решения , но только краткое объяснение из PowerShell * расширение строки (интерполяция строки) :

  • Только переменные сами по себе могут быть встроены напрямую внутри строк в двойных кавычках ("...") (в отличие от строк в одинарных кавычках ('...'), как и во многих других языках, для литерал содержимого).

    • Это относится как к обычным переменным, так и к переменным, ссылающимся на определенное пространство имен; e.g.:
      "var contains: $var", "Path: $env:PATH"

    • Если первый символ после , то имя переменной может быть ошибочно принято за часть имени - что, в частности, включает : - , используйте {...} вокруг имени переменной для устранения неоднозначности ; e.g.:
      "${var}", "${env:PATH}"

    • Чтобы использовать $ как литерал , вы должны экранировать его с помощью `, escape-символа PowerShell; e.g.:
      "Variable `$var"

  • Любой символ после имени переменной, включая [ и ., рассматривается как литерал часть строки, поэтому для индексировать во встроенные переменные ($var[0]) или обращаться к свойству ($var.Count), вам нужно $(...), оператор подвыражения (на самом деле, $(...) позволяет вставлять целые операторы ); например:

    • "1st element: $($var[0])"
    • "Element count: $($var.Count)"
    • "Today's date: $((Get-Date -DisplayHint Date | Out-String).Trim())"
  • Stringification (преобразование в строку) применяется к любому значению переменной / результату оценки, который еще не является строкой:

    • Предостережение : там, где можно применить форматирование, специфичное для культуры, PowerShell выбирает инвариант культура , что в значительной степени совпадает с датой в американском и английском языках. и форматирование чисел ; то есть даты и числа будут представлены в американском формате (например, в формате даты первого месяца и в качестве десятичной отметки .).
    • По сути, метод .ToString() вызывается для любого результирующего нестрокового объекта или коллекции (строго говоря, это .psobject.ToString(), который в некоторых случаях переопределяет .ToString(), особенно для массивов / коллекций и пользовательских объектов PS)

      • Обратите внимание, что это не то же представление, которое вы получаете, когда вы выводите переменную или выражение напрямую, и многие типы имеют нет значимых представлений строки по умолчанию - они просто возвращают свой полный тип имя .
        Однако вы можете встроить $(... | Out-String), чтобы явно применить форматирование вывода PowerShell по умолчанию.
    • Более подробное обсуждение строчения см. В этом ответе моего.


Как указано, с использованием -f оператор форматирования строки (<format-string> -f <arg>[, ...]) является альтернативой интерполяции строки, которая отделяет литеральные части строки от переменной частей:

'1st element: {0}; count: {1:x}'  -f  $var[0], $var.Count
  • Обратите внимание на использование '...' на LHS , потому что строка формата (шаблон) сама по себе является литерал . Использование '...' в этом случае является хорошей привычкой для формирования как для указания намерения использовать буквальное содержимое, так и для возможности встраивания $ символов без экранирования.

  • В дополнение к простым позиционным заполнителям ({0} для 1-го аргумента. {1} для 2-го, ...) , вы можете дополнительно использовать управление форматированием преобразование в строку ; в приведенном выше примере x запрашивает шестнадцатеричное представление числа.
    Доступные форматы см. В документации * .NET 11 Framework * метода , на котором основан оператор -f.

  • Подводный камень: -f имеет высокий приоритет , поэтому обязательно включите выражения RHS, отличные от простого индекса или доступа к свойству, в (...);например, '{0:N2}' -f 1/3 не будет работать должным образом, будет работать только '{0:N2}' -f (1/3).

  • Предостережения : между строкой важные различия интерполяция и -f:

    • В отличие от расширения внутри "...", оператор -f является чувствительным к культуре:

      • Следовательно, следующие два, казалось бы, эквивалентных утверждения не дают одинаковый результат:

        PS> [cultureinfo]::CurrentCulture='fr'; $n=1.2; "expanded: $n"; '-f: {0}' -f $n
        
        expanded: 1.2
        -f: 1,2
        
      • Обратите внимание, что только *Команда, отформатированная в 1223 *, учитывала французскую (fr) десятичную метку (,).
        Опять же, см. Ранее связанный ответ , чтобы увидеть, когда PowerShell является и не является культурой-чувствительный.

    • В отличие от расширения внутри "...", -f структурирует массивы как <type-name>[]:

      PS> $arr = 1, 2, 3; "`$arr: $arr"; '$arr: {0}' -f (, $arr)
      
       $arr: 1 2 3
       $arr: System.Object[]
      
      • Обратите внимание, как "..." интерполяция создала разделенный пробелами список строковых значений всех элементов массива, тогда как -f -форматирование только печатало массивИмя типа.
        (Как уже говорилось, $arr внутри "..." эквивалентно:
        (1, 2, 3).psobject.ToString(), и это обычно невидимый вспомогательный тип [psobject], который обеспечивает дружественное представление.)

      • Также обратите внимание, как (, ...) использовался для переноса массива $arr в вспомогательный массив, который гарантирует, что -f видит выражение как единственный операнд ;по умолчанию элементы массива будут обрабатываться как отдельные операнды.

...