Powershell выводит элементы массива при интерполяции в двойных кавычках

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

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

Output:    
testing

все работает нормально. Но если я помещу вокруг него двойные кавычки:

echo "$test[0]"

Output:
testing[0]

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

3 ответов


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

"$test[0]"

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

"$($test[0])"

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

"{0}" -f $test[0]

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

echo "$($test[0])"

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

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

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


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

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

    • это относится как к регулярным переменным, так и к переменным, ссылающимся на определенное пространство имен; например:
      "var contains: $var", "Path: $env:PATH"

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

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

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

    • "1st element: $($var[0])"
    • "Element count: $($var.Count)"
  • Stringification (преобразование в строку) применяется к любому значению переменной / результату оценки, который еще не является строка:

    • предостережение: если можно применить форматирование, зависящее от культуры, PowerShell выбирает инвариант культура, который во многом совпадает с US-английское форматирование даты и номера; то есть даты и числа будут представлены в US-like формате (например, формат первого месяца и . в качестве десятичного знака).
    • по сути,.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 запрос hex представление числа.
    Доступные форматы см. В документации платформы .NET framework String.Format метод, которым -f оператора на.

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

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


в отличие от интерполяции внутри "..." на -f оператор is чувствительный к культуре:

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

PS> [cultureinfo]::CurrentCulture = 'fr'; $n = 1.2; "interpolated: $n"; '-f: {0}' -f $n

interpolated: 1.2
-f: 1,2

обратите внимание, как только -f - отформатированная команда уважала французов (fr) знак десятичной дроби (,).
Опять же, см. ранее ответ для всестороннего рассмотрения того,когда PowerShell является и не является чувствительным к культуре.


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

PS> $arr = 1, 2, 3; "`$arr: $arr"; '$arr: {0}' -f (, $arr)

$arr: 1 2 3
$arr: System.Object[]

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

обратите внимание, как "..." интерполяция создала разделенный пробелом список stringification всех элементов массива, тогда как -f-форматирование печатается только имя типа массива.
(Как уже говорилось,$arr внутри "..." эквивалентно:
(1, 2, 3).psobject.ToString() и это типа [psobject] это обеспечивает дружественное представление.)