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

Posted

技术标签:

【中文标题】如何在双引号字符串中使用对象的属性?【英文标题】:How can you use an object's property in a double-quoted string? 【发布时间】:2010-11-11 20:31:38 【问题描述】:

我有以下代码:

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

【讨论】:

这行得通,但我想知道我是否可以像我一开始试图避免的那样连接字符串 @ozzy432836 当然可以。或者使用格式字符串。这通常并不重要,取决于个人喜好。【参考方案2】:

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

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

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

...\$LogFileName.ldf

如果对象的属性默认展开,上面的会解析为

...\

由于$LogFileName 引用的对象没有名为ldf 的属性,因此将用$null(或空字符串)替换该变量。

【讨论】:

不错的发现。我实际上并不完全确定他的问题是什么,但试图从一个闻起来很糟糕的字符串中访问属性:) 谢谢大家! Johannes 为什么你认为访问字符串中的属性很难闻?建议如何完成? 穴居人:这只是一个引起我注意的潜在错误原因。您当然可以这样做,这并不奇怪,但是当省略 $() 运算符时,它会尖叫“失败”,因为它无法工作。因此,我的回答只是对您的问题可能是什么的有根据的猜测,使用我能看到的第一个可能的失败原因。对不起,如果它看起来像,但该评论并没有提到任何最佳实践。 很好,你让我担心了! ;) 当我第一次开始使用 powershell 时,我认为字符串中的变量有点奇怪,但它非常方便。我只是没能找到一个可以寻找语法帮助的明确地方。【参考方案3】:

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

用 PowerShell 字符串扩展实用总结补充Joey's helpful answer(双引号中的字符串插值> 字符串("...",又名 可扩展字符串),包括双引号 here-strings):

只有$foo$global:foo(或$script:foo、...)和$env:PATH(环境变量)等引用可以直接 嵌入到"..." 字符串中 - 也就是说,只有变量引用作为一个整体被扩展,而与后面的内容无关。 p>

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

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

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

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

使用$(...) 甚至允许您将整个命令的输出嵌入双引号字符串(例如"Today is $((Get-Date).ToString('d')).")。

插值结果不一定与默认输出格式相同(例如,如果您将变量/子表达式直接打印到控制台,您会看到什么,这涉及默认格式化程序; 见Get-Help about_format.ps1xml):

集合(包括数组)通过在元素的字符串表示形式之间放置一个单个空格(默认情况下;a可以通过设置preference variable $OFS 来指定不同的分隔符,尽管这在实践中很少见)例如,"array: $(@(1, 2, 3))" 产生array: 1 2 3

任何其他类型的实例(包括本身不是集合​​的集合元素)通过 either 调用 IFormattable.ToString() 方法与 invariantculture,如果实例的类型支持IFormattable 接口[1]通过调用.psobject.ToString(),在大多数情况下只是调用底层 .NET 类型的 .ToString() 方法[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"(字符串插值)总是导致 culture-invariant 表示 - 请参阅this answer。

[2] 值得注意的覆盖: • 前面讨论的集合的字符串化(以空格分隔的元素列表,而不是像System.Object[] 这样的东西)。 • [pscustomobject] 实例(解释为here)的散列表like 表示,而不是空字符串

【讨论】:

$.. 有合适的名称吗? @AbrahamZinala,我不知道。也许变量引用与消除歧义名称通过花括号外壳? (开个玩笑。)【参考方案4】:

@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

【讨论】:

是的,这更具可读性,特别是如果您习惯于 .net 代码。谢谢! :) 第一次使用格式字符串时需要注意一个问题,即您经常需要将表达式括在括号中。例如,您不能简单地编写write-host "foo = 0" -f $foo,因为PowerShell 会将-f 视为write-hostForegroundColor 参数。在此示例中,您需要write-host ( "foo = 0" -f $foo )。这是标准的 PowerShell 行为,但值得注意。 只是为了迂腐,上面的例子不是“字符串插值”,而是“复合格式化”。由于 Powershell 与 C# 和 VB.NET 是主要编程语言的 .NET 密不可分,因此术语“字符串插值”和“插值字符串”(以及任何类似内容)应仅适用于在那些中发现的新 $ 前置字符串语言(C# 6 和 VB.NET 14)。在这些字符串中,参数表达式直接嵌入到字符串中,而不是数字参数引用,然后实际表达式作为String.Format 的参数。 @andyb,关于格式字符串“gotchas”。这实际上是 ExpressionArgument 模式之间的区别(参见about_Parsing)。命令被解析成tokens(一组字符组成一个有意义的字符串)。空格分隔将合并但被忽略的标记。如果命令的第一个标记是数字、变量、语句关键字(if、while 等)、一元运算符、 等...则解析模式为 Expression 否则为 Argument(最多为命令终止符)。 【参考方案5】:

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

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

【讨论】:

以上是关于如何在双引号字符串中使用对象的属性?的主要内容,如果未能解决你的问题,请参考以下文章

如何在双引号中打印字符串? (不打印双引号)

Python,在双引号之间的字符串中检测带有引号的字符[关闭]

在双引号内转义双引号

正则表达式在双引号内转义双引号

无法使用正则表达式在双引号之间提取字符串

字符串