Как вы можете использовать свойство объекта в строке с двумя кавычками?
У меня есть следующий код:
$DatabaseSettings = @();
$NewDatabaseSetting = "" | select DatabaseName, DataFile, LogFile, LiveBackupPath;
$NewDatabaseSetting.DatabaseName = "LiveEmployees_PD";
$NewDatabaseSetting.DataFile = "LiveEmployees_PD_Data";
$NewDatabaseSetting.LogFile = "LiveEmployees_PD_Log";
$NewDatabaseSetting.LiveBackupPath = '\\LiveServer\LiveEmployeesBackups';
$DatabaseSettings += $NewDatabaseSetting;
Когда я пытаюсь использовать одно из свойств в команде выполнения строки:
& "$SQlBackupExePath\SQLBackupC.exe" -I $InstanceName -SQL `
"RESTORE DATABASE $DatabaseSettings[0].DatabaseName FROM DISK = '$tempPath\$LatestFullBackupFile' WITH NORECOVERY, REPLACE, MOVE '$DataFileName' TO '$DataFilegroupFolder\$DataFileName.mdf', MOVE '$LogFileName' TO '$LogFilegroupFolder\$LogFileName.ldf'"
Он пытается просто использовать значение $DatabaseSettings
, а не значение $DatabaseSettings[0].DatabaseName
, что недействительно.
Мое обходное решение состоит в том, чтобы оно было скопировано в новую переменную.
Как я могу получить доступ к свойству объекта непосредственно в строке с двумя кавычками?
Ответы
Ответ 1
Когда вы заключаете имя переменной в строку в двойных кавычках, оно будет заменено значением этой переменной:
$foo = 2
"$foo"
становится
"2"
Если вы не хотите, чтобы вы использовали одинарные кавычки:
$foo = 2
'$foo'
Однако, если вы хотите получить доступ к свойствам или использовать индексы для переменных в строке в двойных кавычках, вы должны заключить это подвыражение в $()
:
$foo = 1,2,3
"$foo[1]" # yields "1 2 3[1]"
"$($foo[1])" # yields "2"
$bar = "abc"
"$bar.Length" # yields "abc.Length"
"$($bar.Length)" # yields "3"
PowerShell только расширяет переменные в этих случаях, не более того. Для принудительной оценки более сложных выражений, включая индексы, свойства или даже полные вычисления, вы должны заключить их в оператор подвыражения $( )
который вызывает вычисление выражения внутри и встраивание в строку.
Ответ 2
@Joey имеет правильный ответ, но просто добавлю немного больше о том, почему вам нужно заставить оценку с помощью $()
:
В вашем примере кода содержится неопределенность, указывающая на то, что создатели PowerShell, возможно, решили ограничить расширение до простых ссылок на переменные, а также не поддерживать доступ к свойствам (в качестве расширения в сторону: расширение строки выполняется путем вызова ToString()
метод на объекте, который может объяснить некоторые "нечетные" результаты).
Ваш пример содержится в самом конце командной строки:
...\$LogFileName.ldf
Если свойства объектов были расширены по умолчанию, то выше будет разрешено
...\
поскольку объект, на который ссылается $LogFileName
, не будет иметь свойство с именем ldf
, $null
(или пустая строка), будет заменено переменной.
Ответ 3
@Joey имеет хороший ответ. Существует еще один способ с большим представлением .NET с эквивалентом String.Format, я предпочитаю его при доступе к свойствам объектов:
Вещи о машине:
$properties = @{ 'color'='red'; 'type'='sedan'; 'package'='fully loaded'; }
Создать объект:
$car = New-Object -typename psobject -Property $properties
Интерполировать строку:
"The {0} car is a nice {1} that is {2}" -f $car.color, $car.type, $car.package
Выходы:
# The red car is a nice sedan that is fully loaded
Ответ 4
Documentation note: [TG40] covers string interpolation, but, as of PSv5, not in-depth.
Дополнить полезный ответ джои with a прагматической сводкой PowerShell раскрытие строки (интерполяция строк в строках с двойными кавычками ("...")
, в том числе в двойных кавычках вот-строки):
Только ссылки, такие как $foo
, $global:foo
(или $script:foo
,...) и $env:PATH
(переменные среды), распознаются, когда они непосредственно встроены в строку "..."
, то есть раскрывается только сама ссылка на переменную, независимо от того, что из этого следует.
Чтобы устранить неоднозначность имени переменной из последующих символов в строке, заключите его в {
и }
; например, ${foo}
.
Это особенно важно, если за именем переменной следует :
, поскольку в противном случае PowerShell рассмотрел бы все между $
и :
спецификатором области видимости, что обычно приводит к сбою интерполяции; например, "$HOME: where the heart is."
ломается, но "${HOME}: where the heart is."
работает как задумано.
(В качестве альтернативы '
-escape :
: "$HOME': where the heart is."
).
Чтобы трактовать $
или "
как литерал, добавьте к нему escape-символ. '
(обратный удар); например.:
"'$HOME value: '"$HOME'""
Для чего-либо еще, включая использование индексов массива и доступ к переменной объекта properties, вы должны заключить выражение в $(...)
, оператор подвыражения (например, "PS version: $($PSVersionTable.PSVersion)"
или "1st el.: $($someArray[0])"
)
- Использование
$(...)
даже позволяет встроить вывод всей командной строки в строки в двойных кавычках (например, "Today is $((Get-Date).ToString('d'))."
).
Результаты интерполяции не обязательно выглядят так же, как и выходной формат по умолчанию (что вы увидите, если вы распечатаете переменную/подвыражение, например, непосредственно на консоли, что включает форматер по умолчанию; см. Get-Help about_format.ps1xml
):
Коллекции, включая массивы, преобразуются в строки, помещая один пробел между строковыми представлениями элементов (по умолчанию; другой разделитель можно указать, установив $OFS
) Например, "array: $(@(1, 2, 3))"
дает array: 1 2 3
Экземпляры любого другого типа (включая элементы коллекций, которые сами не являются коллекциями) преобразуются в строку, либо вызывая метод IFormattable.ToString()
с инвариантной культурой, если экземпляр Тип поддерживает интерфейс IFormattable
[1] или вызывая .psobject.ToString()
, который в большинстве случаев просто вызывает базовый метод .NET type .ToString()
[2], который может давать или не давать значимое представление: если (не примитивный) тип специально не переопределил метод .ToString()
, вы получите только полное имя типа (например, "hashtable: $(@{ key = 'value' })"
выдает hashtable: System.Collections.Hashtable
).
Чтобы получить тот же вывод, что и в консоли, используйте подвыражение и pipeу для Out-String
и примените .Trim()
для удаления любых начальных и конечных пустых строк, если это необходимо; например,
"hashtable:'n$((@{ key = 'value' } | Out-String).Trim())"
дает:
hashtable:
Name Value
---- -----
key value
[1] This perhaps surprising behavior means that, for types that support culture-sensitive representations, [TG442] yields a current-culture-appropriate representation, whereas [TG443] (string interpolation) always results in a culture-invariant representation - see this answer of mine.
[2] Notable overrides:
* The previously discussed stringification of collections (space-separated list of elements rather than something like [TG444]).
* The hashtable-like representation of [TG445] instances (explained here) rather than the empty string.