¿Cómo se puede utilizar la propiedad de un objeto en una cadena entre comillas dobles?
Tengo el siguiente código:
$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;
Cuando intento usar una de las propiedades en una cadena, ejecuto el comando:
& "$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'"
Intenta utilizar simplemente el valor de $DatabaseSettings
en lugar del valor de $DatabaseSettings[0].DatabaseName
, lo cual no es válido.
Mi solución es copiarlo en una nueva variable.
¿Cómo puedo acceder a la propiedad del objeto directamente en una cadena entre comillas dobles?
Cuando incluya el nombre de una variable entre comillas dobles, será reemplazado por el valor de esa variable:
$foo = 2
"$foo"
se convierte
"2"
Si no quieres eso tienes que usar comillas simples:
$foo = 2
'$foo'
Sin embargo, si desea acceder a propiedades o utilizar índices en variables en una cadena entre comillas dobles, debe encerrar esa subexpresión en $()
:
$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 sólo expande las variables en esos casos, nada más. Para forzar la evaluación de expresiones más complejas, incluidos índices, propiedades o incluso cálculos completos, debe incluirlas en el operador de subexpresión, $( )
lo que hace que la expresión interna se evalúe y se incruste en la cadena.
Nota de documentación: Get-Help about_Quoting_Rules
cubre la interpolación de cadenas, pero, a partir de PSv5, no en profundidad.
Para complementar la útil respuesta de Joey con un resumen pragmático de la expansión de cadenas de PowerShell (interpolación de cadenas en cadenas entre comillas dobles ( "..."
, también conocidas como cadenas expandibles ), incluidas cadenas aquí entre comillas dobles :
Solo referencias como
$foo
,$global:foo
(o$script:foo
, ...) y$env:PATH
(variables de entorno) se pueden incrustar directamente"..."
en una cadena ; es decir, solo se expande la referencia de la variable en sí, en su conjunto , independientemente de lo que sigue.Por ejemplo,
"$HOME.foo"
se expande a algo comoC:\Users\jdoe.foo
, porque la.foo
parte se interpretó literalmente , no como un acceso a la propiedad.Para eliminar la ambigüedad de un nombre de variable de los caracteres posteriores de la cadena, enciérrelo entre
{
y}
; p.ej,${foo}
.
Esto es especialmente importante si el nombre de la variable va seguido de a:
, ya que, de lo contrario, PowerShell consideraría todo lo que se encuentre entre y$
a:
, lo que normalmente provocaría que fallara la interpolación ; por ejemplo, se rompe, pero funciona según lo previsto. (Como alternativa, escape de : , pero eso solo funciona si el carácter que sigue al nombre de la variable no forma accidentalmente una secuencia de escape con un precedente , como por ejemplo , consulte el tema de ayuda conceptual about_Special_Characters )."$HOME: where the heart is."
"${HOME}: where the heart is."
`
:
"$HOME`: where the heart is."
`
`b
Para tratar a
$
o a"
como un literal , antepóngale un carácter de escape.`
(una comilla invertida ); p.ej:
"`$HOME's value: $HOME"
Para cualquier otra cosa, incluido el uso de subíndices de matriz y el acceso a las propiedades de una variable de objeto , debe encerrar la expresión entre
$(...)
el operador de subexpresión (por ejemplo,"PS version: $($PSVersionTable.PSVersion)"
o"1st el.: $($someArray[0])"
)- El uso
$(...)
incluso le permite incrustar la salida de comandos completos en cadenas entre comillas dobles (por ejemplo,"Today is $((Get-Date).ToString('d'))."
).
- El uso
Los resultados de la interpolación no necesariamente tienen el mismo aspecto que el formato de salida predeterminado (lo que vería si imprimiera la variable/subexpresión directamente en la consola, por ejemplo, lo que involucra el formateador predeterminado; consulte
Get-Help about_format.ps1xml
):Las colecciones , incluidas las matrices, se convierten en cadenas colocando un único espacio entre las representaciones de cadena de los elementos (de forma predeterminada; se puede especificar un separador diferente configurando la variable de preferencia
$OFS
, aunque esto rara vez se ve en la práctica). Por ejemplo,"array: $(@(1, 2, 3))"
producearray: 1 2 3
Las instancias de cualquier otro tipo (incluidos elementos de colecciones que no son en sí mismas colecciones) se encadenan llamando al
IFormattable.ToString()
método con la cultura invariante , si el tipo de instancia admite laIFormattable
interfaz [1] , o llamando a.psobject.ToString()
, que en la mayoría de los casos simplemente invoca el.ToString()
método del tipo .NET subyacente [2] , que puede o no dar una representación significativa: a menos que un tipo (no primitivo) haya anulado específicamente el.ToString()
método, todo lo que obtendrá es el nombre completo del tipo (por ejemplo,"hashtable: $(@{ key = 'value' })"
producehashtable: System.Collections.Hashtable
).Para obtener el mismo resultado que en la consola , use una subexpresión en la que canalice
Out-String
y aplique.Trim()
para eliminar cualquier línea vacía inicial y final, si lo desea; por ejemplo,
"hashtable:`n$((@{ key = 'value' } | Out-String).Trim())"
produce:hashtable: Name Value ---- ----- key value
[1] Este comportamiento quizás sorprendente significa que, para los tipos que admiten representaciones sensibles a la cultura, $obj.ToString()
se obtiene una representación actual apropiada para la cultura, mientras que "$obj"
(interpolación de cadenas) siempre da como resultado una representación invariante para la cultura ; consulte esta respuesta .
[2] Anulaciones notables:
• La cadena de colecciones discutida anteriormente (lista de elementos separados por espacios en lugar de algo como System.Object[]
).
• La representación de instancias similar[pscustomobject]
a una tabla hash (explicada aquí ) en lugar de la cadena vacía .
@Joey tiene la respuesta correcta, pero solo para agregar un poco más de por qué necesitas forzar la evaluación con $()
:
Su código de ejemplo contiene una ambigüedad que indica por qué los creadores de PowerShell pueden haber elegido limitar la expansión a meras referencias de variables y no admitir el acceso a las propiedades también (aparte: la expansión de cadenas se realiza llamando al ToString()
método en el objeto, que puede explicar algunos resultados "extraños").
Su ejemplo contenido al final de la línea de comando:
...\$LogFileName.ldf
Si las propiedades de los objetos se expandieran de forma predeterminada, lo anterior se resolvería como
...\
dado que el objeto al que hace referencia $LogFileName
no tendría una propiedad llamada ldf
, $null
(o una cadena vacía) se sustituiría por la variable.
@Joey tiene una buena respuesta. Hay otra forma con una apariencia más .NET con un equivalente String.Format, la prefiero cuando accedo a propiedades de objetos:
Cosas sobre un coche:
$properties = @{ 'color'='red'; 'type'='sedan'; 'package'='fully loaded'; }
Crea un objeto:
$car = New-Object -typename psobject -Property $properties
Interpolar una cadena:
"The {0} car is a nice {1} that is {2}" -f $car.color, $car.type, $car.package
Salidas:
# The red car is a nice sedan that is fully loaded
Si desea utilizar propiedades entre comillas, siga lo siguiente. Tienes que usar $ fuera del corchete para imprimir la propiedad.
$($variable.property)
Ejemplo:
$uninstall= Get-WmiObject -ClassName Win32_Product |
Where-Object {$_.Name -like "Google Chrome"
Producción:
IdentifyingNumber : {57CF5E58-9311-303D-9241-8CB73E340963}
Name : Google Chrome
Vendor : Google LLC
Version : 95.0.4638.54
Caption : Google Chrome
Si solo desea la propiedad de nombre, haga lo siguiente:
"$($uninstall.name) Found and triggered uninstall"
Producción:
Google Chrome Found and triggered uninstall