Как вы можете использовать свойство объекта в строке в двойных кавычках? - PullRequest
76 голосов
/ 18 июля 2009

У меня есть следующий код:

$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, что недопустимо.
Мой обходной путь - скопировать его в новую переменную.

Как получить доступ к свойству объекта напрямую в строке в двойных кавычках?

Ответы [ 4 ]

134 голосов
/ 18 июля 2009

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

$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 только расширяет переменные в этих случаях, не более того. Для принудительной оценки более сложных выражений, включая индексы, свойства или даже полные вычисления, вы должны заключить их в оператор подвыражения $( ), который вызывает вычисление выражения внутри и встраивание в строку.

12 голосов
/ 18 июля 2009

@ Джои имеет правильный ответ, но просто добавлю еще немного, почему вам нужно форсировать оценку с помощью $():

Ваш пример кода содержит неоднозначность, которая указывает на то, почему создатели PowerShell, возможно, решили ограничить расширение только ссылками на переменные и не поддерживать доступ к свойствам (например, расширение строки выполняется путем вызова ToString() метод на объекте, который может объяснить некоторые «странные» результаты).

Ваш пример содержится в самом конце командной строки:

...\$LogFileName.ldf

Если свойства объектов были расширены по умолчанию, приведенное выше разрешит до

...\

, поскольку объект, на который ссылается $LogFileName, не будет иметь свойства с именем ldf, вместо переменной будет подставляться $null (или пустая строка).

8 голосов
/ 29 июля 2016

@ У Джои хороший ответ. Есть и другой способ - более внешний вид .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
6 голосов
/ 06 ноября 2016

Примечание к документации: Get-Help about_Quoting_Rules охватывает интерполяцию строки, но, начиная с PSv5, не углубленно.

В дополнение полезный ответ Джои с прагматической сводкой из PowerShell расширение строки (интерполяция строки в двойные кавычки строк, в том числе в двойных кавычках здесь-строк ):

  • Только ссылки, такие как $foo, $global:foo (или $script:foo, ...) и $env:PATH (переменные среды) распознаются, когда напрямую встроенный в "..." строку - то есть только ссылка на переменную сама раскрывается независимо от того, что следует.

    • Чтобы устранить неоднозначность имени переменной из последующих символов в строке, заключить ее в { и }; например, ${foo}.
      Это особенно важно, если за именем переменной следует :, поскольку в противном случае PowerShell будет учитывать все, что находится между $ и : a scope , что обычно вызывает интерполяцию до терпеть неудачу ; например, "$HOME: where the heart is." ломается, но "${HOME}: where the heart is." работает как задумано.
      (В качестве альтернативы ` -сценарий :: "$HOME`: where the heart is.").

    • Чтобы трактовать $ или " как литерал , добавьте перед ним escape-символ. ` ( backtick ); e.g.:
      "`$HOME's value: `"$HOME`""

  • Для чего-либо еще, включая использование индексов массива и доступ к свойствам переменной объекта, вы должны заключить выражение в $(...), оператор подвыражения (например, "PS version: $($PSVersionTable.PSVersion)" или "1st el.: $($someArray[0])")

    • Использование $(...) даже позволяет встроить вывод всей командной строки в строки в двойных кавычках (например, "Today is $((Get-Date).ToString('d')).").
  • Результаты интерполяции не обязательно выглядят так же, как и выходной формат по умолчанию (что вы увидите, если вы распечатаете переменную / подвыражение, например, непосредственно на консоли, что включает форматтер по умолчанию; см. Get-Help about_format.ps1xml):

    • Коллекции , включая массивы, преобразуются в строки , помещая один пробел между строковыми представлениями элементов (по умолчанию; a другой разделитель можно указать, установив $OFS) Например, "array: $(@(1, 2, 3))" выход array: 1 2 3

    • Экземпляры любого другого типа (включая элементы коллекций, которые не являются самими коллекциями) имеют строковое значение либо , вызывая IFormattable.ToString() метод с инвариантной культурой , если тип экземпляра поддерживает IFormattable интерфейс [1] , или , вызывая .psobject.ToString(), что в В большинстве случаев просто вызывается метод .ToString() базового типа .NET [2] , который может давать или не давать значимое представление: если (не примитивный) тип специально не переопределил .ToString() метод, все, что вы получите, это полное имя типа (например, "hashtable: $(@{ key = 'value' })" выход hashtable: System.Collections.Hashtable).

    • Чтобы получить тот же вывод, что и в консоли , использовать подвыражение и трубу для Out-String и применить .Trim() для удаления любых начальных и конечных пустых строк при желании; например.,
      "hashtable:`n$((@{ key = 'value' } | Out-String).Trim())" выход:

      hashtable:                                                                                                                                                                          
      Name                           Value                                                                                                                                               
      ----                           -----                                                                                                                                               
      key                            value      
      

[1] Это, возможно, удивительное поведение означает, что для типов, которые поддерживают представления, чувствительные к культуре, $obj.ToString() дает текущее -культурно-соответствующее представление, тогда как "$obj" (интерполяция строки ) всегда приводит к представлению , инвариантному к культуре - см. этот ответ моего.

[2] Заметные переопределения:
* Обсуждаемая ранее последовательность коллекций (разделенный пробелами список элементов, а не что-то вроде System.Object[]).
* Хеш-таблица - как представление [pscustomobject] экземпляров (объяснено здесь ) вместо пустой строки .

...