126

我有以下代码:

$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

5 回答 5

206

当您将变量名称括在双引号字符串中时,它将被该变量的值替换:

$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 仅在这些情况下扩展变量,仅此而已。要强制计算更复杂的表达式,包括索引、属性甚至完整的计算,您必须将它们包含在子表达式运算符$( )中,这会导致内部表达式被计算并嵌入到字符串中。

于 2009-07-17T21:41:57.493 回答
18

@Joey 有正确的答案,但只是补充一点,说明为什么需要强制评估$()

您的示例代码包含一个歧义,指出为什么 PowerShell 的制造商可能选择将扩展限制为仅仅是变量引用而不支持对属性的访问(顺便说一句:字符串扩展是通过调用ToString()对象上的方法来完成的,这可以解释一些“奇怪”的结果)。

您的示例包含在命令行的最后:

...\$LogFileName.ldf

如果默认情况下扩展对象的属性,则上述内容将解析为

...\

因为被引用的对象$LogFileName不会有一个名为ldf,的属性$null(或一个空字符串)将被替换为变量。

于 2009-07-17T22:19:07.167 回答
16

文档说明:Get-Help about_Quoting_Rules涵盖字符串插值,但从 PSv5 开始,并不深入。

为了补充Joey 的有用答案,对 PowerShell 的字符串扩展(双引号字符串中的字符串插值(又称可扩展字符串),包括双引号here-strings中的字符串插值)进行了实用总结"..."

  • 只有$foo, $global:foo(or $script:foo, ...) 和$env:PATH(environment variables)之类的引用才能 直接嵌入到"..."字符串中——也就是说,只有变量引用本身作为一个整体被扩展,而与后面的内容无关。

    • 例如,"$HOME.foo"扩展为类似C:\Users\jdoe.foo,因为该.foo部分是按字面意思解释的-而不是作为属性访问。

    • 区分变量名与字符串中的后续字符,请将其括在{and}中;例如,${foo}
      如果变量名称后跟 a :,这一点尤其重要,因为 PowerShell 否则会考虑介于 the$:a范围说明符之间的所有内容,通常会导致插值失败;例如,"$HOME: where the heart is."中断,但"${HOME}: where the heart is."按预期工作。
      (或者,`- 转义:: "$HOME`: where the heart is.",但这仅在变量名后面的字符不会意外形成带有前面的转义序列`时才有效,例如`b- 请参阅概念about_Special_Characters帮助主题)。

    • 要将 a$或 a"视为文字,请在其前面加上转义字符。`反引号);例如:
      "`$HOME's value: $HOME"

  • 对于其他任何事情,包括使用数组下标和访问对象变量的属性,您必须将表达式括在$(...)子表达式运算符(例如,"PS version: $($PSVersionTable.PSVersion)""1st el.: $($someArray[0])")中

    • 使用$(...)even 允许您将整个命令的输出嵌入双引号字符串(例如,"Today is $((Get-Date).ToString('d')).")中。
  • 插值结果不一定与默认输出格式相同(例如,如果您将变量/子表达式直接打印到控制台,您会看到什么,这涉及默认格式化程序;请参阅Get-Help about_format.ps1xml):

    • 集合,包括数组,通过在元素的字符串表示之间放置一个空格来转换为字符串(默认情况下;可以通过设置首选项变量$OFS来指定不同的分隔符,尽管这在实践中很少见)例如,"array: $(@(1, 2, 3))"yieldarray: 1 2 3

    • 任何其他类型的实例(包括本身不是集合​​的集合元素)通过调用具有不变区域性的方法(如果实例的类型支持接口[1]调用(在大多数情况下只是调用)来字符串化底层 .NET 类型的方法IFormattable.ToString()IFormattable.psobject.ToString().ToString()[2],它可能会或可能不会给出有意义的表示:除非(非原始)类型专门覆盖了该.ToString()方法,否则您将得到的只是完整的类型名称(例如,"hashtable: $(@{ key = 'value' })"yield hashtable: System.Collections.Hashtable)。

    • 要获得与console中相同的输出,请使用一个子表达式,您可以在其中使用管道Out-String并应用.Trim()以删除任何前导和尾随空行(如果需要);例如,
      "hashtable:`n$((@{ key = 'value' } | Out-String).Trim())"产生:

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

[1] 这种可能令人惊讶的行为意味着,对于支持文化敏感表示的类型,会$obj.ToString()产生当前的适合文化的表示,而"$obj"(字符串插值)总是会产生文化不变的表示 - 请参阅此答案

[2] 值得注意的覆盖:
• 前面讨论的集合字符串化(以空格分隔的元素列表,而不是类似的东西System.Object[])。•实例的类似哈希表的表示(
此处解释而不是空字符串[pscustomobject]

于 2016-11-06T04:29:12.490 回答
11

@Joey 有一个很好的答案。还有另一种方式,使用等效的 String.Format 具有更多 .NET 外观,我更喜欢在访问对象的属性时使用它:

汽车的相关资料:

$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
于 2016-07-29T18:28:55.537 回答
2

如果您想在引号内使用属性,请按照以下说明进行操作。您必须在括号外使用 $ 来打印属性。

$($variable.property)

例子:

$uninstall= Get-WmiObject -ClassName Win32_Product |
    Where-Object {$_.Name -like "Google Chrome"

输出:

IdentifyingNumber : {57CF5E58-9311-303D-9241-8CB73E340963}
Name              : Google Chrome
Vendor            : Google LLC
Version           : 95.0.4638.54
Caption           : Google Chrome

如果您只想要名称属性,请执行以下操作:

"$($uninstall.name) Found and triggered uninstall"

输出:

Google Chrome Found and triggered uninstall
于 2021-10-22T22:36:52.957 回答