如何在双引号字符串中使用对象的属性?


96

我有以下代码:

$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;

当我尝试在字符串execute命令中使用属性之一时:

& "$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,这是无效的。
我的解决方法是将其复制到新变量中。

如何直接在双引号中访问对象的属性?

Answers:


163

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

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


这行得通,但我想知道我是否应该像最初那样尽量避免串联字符串
ozzy432836 '16

2
@ ozzy432836当然可以。或使用格式字符串。通常并不重要,而是取决于个人喜好。
乔伊,

15

@Joey给出了正确的答案,但只是增加了一些原因,您为什么需要使用以下方法强制执行评估$()

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

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

...\$LogFileName.ldf

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

...\

因为由所引用的对象$LogFileName不会有所谓的属性ldf$null(或空字符串)将被代入变量。


1
好发现。我实际上不是很确定他的问题是什么,但是试图从字符串中访问属性听起来很糟糕:)
Joey

多谢你们!约翰尼斯(Johannes)为什么认为访问字符串中的属性有不好的味道?如何建议完成?
Caveman_dick

穴居人:这是引起我误解的潜在错误原因。您当然可以做到这一点,但这并不奇怪,但是当省略$()运算符时,它会尖叫“失败”,因为它无法工作。因此,我的答案只是使用我可能看到的第一个可能的失败原因,对您的问题可能是有根据的猜测。抱歉,它看起来像,但此评论未提及任何最佳实践。
乔伊

好,你让我担心在那里!;)刚开始使用Powershell时,我认为字符串中的变量有点怪异,但是非常方便。我只是没有找到一个可以寻求语法帮助的明确地方。
Caveman_dick

9

@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

是的,尤其是如果您习惯使用.net代码,则更具可读性。谢谢!:)
Caveman_dick '16

3
初次使用格式字符串时要注意一点,这就是您经常需要将表达式括在括号中。例如,您不能简单地编写代码,write-host "foo = {0}" -f $foo因为PowerShell将被-f视为的ForegroundColor参数write-host。在此示例中,您将需要write-host ( "foo = {0}" -f $foo )。这是标准的PowerShell行为,但值得注意。
andyb '16

只是为了学究,上面的示例不是“字符串插值”,而是“复合格式”。由于Powershell与C#和VB.NET是主要编程语言的.NET有着千丝万缕的联系,因此术语“字符串插值”和“插值字符串”(以及类似内容)应仅应用于在这些字符串中找到的新$前缀字符串语言(C#6和VB.NET 14)。在这些字符串中,参数表达式直接嵌入在字符串中,而不是数字参数引用,而实际表达式则作为的参数String.Format
Uber Kluger

@andyb,关于格式字符串“ gotchas”。这实际上是ExpressionArgument模式之间的区别(请参阅about_Parsing)。命令被解析为标记(形成有意义字符串的字符组)。空格分隔将合并但将被忽略的令牌。如果命令的第一个标记是数字,变量,语句关键字(if,while等),一元运算符,{等...,则解析模式为Expression其他方式Argument(由命令终止符决定)。
Uber Kluger

8

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

为了以实用的PowerShell 字符串扩展摘要(用双引号字符串(包括双引号here-strings)进行字符串插值)来补充Joey的有用答案"...")

  • 直接嵌入到字符串中时,仅识别诸如$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.")。

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

  • 对于其他任何事情,包括使用数组下标和访问对象变量的属性,都必须将表达式括$(...)子表达式运算符(例如"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

    • 通过使用不变文化调用方法(如果实例的类型支持接口[1]调用,可以任何其他类型的实例(包括本身不是集合​​的集合的元素)进行字符串化,或者在大多数情况下通过调用IFormattable.ToString()IFormattable.psobject.ToString().ToString()来进行字符串化底层.NET类型的方法[2],它可能会或可能不会给出有意义的表示:除非(非原始)类型明确覆盖了该.ToString()方法,否则您将获得的是完整的类型名(例如"hashtable: $(@{ key = 'value' })"yields 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]在此处说明),而不是空字符串

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.