Powershell выводит элементы массива при интерполяции в двойных кавычках
Я нашел странное поведение в Powershell, окружающем массивы и двойные кавычки. Если я создам и печатаю первый элемент в массиве, например:
$test = @('testing')
echo $test[0]
Output:
testing
Все работает нормально. Но если я поставлю туда двойные кавычки:
echo "$test[0]"
Output:
testing[0]
Была оценена только переменная $ test, и маркер массива [0] обрабатывался буквально как строка. Легкое исправление заключается в том, чтобы просто избегать интерполирования переменных массива в двойных кавычках или сначала назначить их другой переменной. Но мне было интересно, если это поведение по дизайну?
Ответы
Ответ 1
Поэтому, когда вы используете интерполяцию, по умолчанию она интерполирует только следующую переменную в toto. Поэтому, когда вы это делаете:
"$test[0]"
Он воспринимает $ test как следующую переменную, он понимает, что это массив и что у него нет хорошего способа отображения массива, поэтому он решает, что он не может интерполировать и просто отображает строку в виде строки. Решение состоит в том, чтобы явно указать powershell, где бит для интерполяции начинается и где он останавливается:
"$($test[0])"
Обратите внимание, что это поведение является одной из моих основных причин использования форматированных строк вместо того, чтобы полагаться на интерполяцию:
"{0}" -f $test[0]
Ответ 2
В таких случаях вам нужно:
echo "$($test[0])"
Другой альтернативой является использование форматирования строк
echo "this is {0}" -f $test[0]
Обратите внимание, что это будет случай, когда вы также получаете доступ к свойствам в строках. Как "$a.Foo"
- должен быть записан как "$($a.Foo)"
Ответ 3
Полезный ответ EBGreen содержит эффективные решения, но лишь поверхностное объяснение интерполяции строк PowerShell (расширение строки):
-
Только переменные в целом могут быть встроены непосредственно внутри "..."
(строки с двойными кавычками, в отличие от строк с одиночными кавычками ('...'
), как и во многих других языках, для буквального содержимого).
-
Это относится как к обычным переменным, так и к переменным, ссылающимся на конкретное пространство имен; например:
"var contains: $var"
, "Path: $env:PATH"
-
Если первый символ после имени переменной может быть ошибочно принят за часть имени, что особенно включает в себя :
- используйте {...}
вокруг имени переменной для устранения неоднозначности; например:
"${var}"
, "${env:PATH}"
-
Для того, чтобы использовать $
как литерал, вы должны экранировать его '
, PowerShell бежать характер; например:
"Variable '$var"
-
Любой символ после имени переменной - включая [
и .
рассматривается как буквальная часть строки, поэтому для индексации во встроенные переменные ($var[0]
) или доступа к свойству ($var.Count
) вам нужен $(...)
, оператор подвыражения (в факт, $(...)
позволяет вставлять целые утверждения); например:
-
"1st element: $($var[0])"
-
"Element count: $($var.Count)"
-
Stringification (преобразование в строку) применяется к любому результату переменной/оценке, который еще не является строкой:
- Предостережение: в случае применения форматирования, специфичного для культуры, PowerShell выбирает инвариантную культуру, которая во многом совпадает с форматированием даты и числа в США и Англии; то есть, даты и номера будут представлены в США, как формат (например, месяц -f IRST дату и формат
.
в качестве десятичного знака). -
По сути, метод .ToString()
вызывается для любого результирующего .ToString()
объекта или коллекции (строго говоря, это .psobject.ToString()
, который в некоторых случаях переопределяет .ToString()
, особенно для массивов/коллекций и PS пользовательские объекты)
- Обратите внимание, что это не то же представление, которое вы получаете, когда вы выводите переменную или выражение напрямую, а многие типы не имеют значимых представлений строк по умолчанию - они просто возвращают полное имя типа.
Однако вы можете встраивать $(... | Out-String)
, чтобы явно применять форматирование вывода по умолчанию PowerShell.
-
Более подробное обсуждение строения см. В моем ответе.
Как указано, используя -f
, оператор строки -f ormatting (<format-string> -f <arg>[,...]
) является альтернативой строковой интерполяции, которая отделяет литеральные части строки от переменные части:
'1st element: {0}; count: {1:x}' -f $var[0], $var.Count
-
Обратите внимание на использование '...'
на LHS, потому что строка формата (шаблон) сама является литералом. Использование '...'
в этом случае - хорошая привычка формироваться, как сигнализировать о намерении использовать литеральное содержимое, так и возможность встраивания $
символов без экранирования.
-
В дополнение к простым позиционным заполнителям ({0}
для 1-го аргумента. {1}
для второго,...), вы можете дополнительно использовать больше форматирования для преобразования в строку; в приведенном выше примере x
запрашивает шестнадцатеричное представление числа.
Для доступных форматов см. Документацию метода.NET Framework String.Format
, на котором -f
оператор -f
.
-
Pitfall: -f
имеет высокий приоритет, поэтому обязательно включайте выражения RHS, отличные от простого индекса или доступа к свойствам в (...)
; например, '{0:N2}' -f 1/3
не будет работать, как предполагалось, только '{0:N2}' -f (1/3)
-
Предостережения. Существуют важные различия между интерполяцией строк и -f
- см. Ниже.
В отличие от интерполяции внутри "..."
, оператор -f
чувствителен к культуре:
Поэтому следующие два, казалось бы, эквивалентных утверждения не дают такого же результата:
PS> [cultureinfo]::CurrentCulture = 'fr'; $n = 1.2; "interpolated: $n"; '-f: {0}' -f $n
interpolated: 1.2
-f: 1,2
Обратите внимание, что только команда -f
-f одобрена французской (fr
) десятичной меткой (,
).
Опять же, см. Ранее связанный ответ для всестороннего просмотра, когда PowerShell является и не чувствителен к культуре.
В отличие от интерполяции внутри "..."
, -f
массивы как <type-name>[]
:
PS> $arr = 1, 2, 3; "'$arr: $arr"; '$arr: {0}' -f (, $arr)
$arr: 1 2 3
$arr: System.Object[]
Примечание: (,...)
обертывает $arr
в вспомогательный массив, который гарантирует, что -f
видит выражение как единый операнд с массивом;по умолчанию элементы массива будут рассматриваться как отдельные операнды.
Обратите внимание, что интерполяция "..."
создала список строковых разделителей всех элементов массива, в то время как -f
-f ormatting напечатало только имя типа массива.
(Как обсуждалось, $arr
внутри "..."
эквивалентно:
(1, 2, 3).psobject.ToString()
и это тип [psobject]
который обеспечивает дружественное представление.)