2009-07-17 22 views
58

tengo el siguiente código:¿Cómo se puede usar la propiedad de un objeto en una cadena de comillas dobles?

$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 trato de utilizar una de las propiedades de una cadena de ejecutar 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'" 

Se trata de sólo tiene que utilizar el valor de $DatabaseSettings en lugar del valor de $DatabaseSettings[0].DatabaseName, que no es válido.
Mi solución consiste en copiarlo en una nueva variable.

¿Cómo puedo acceder a la propiedad del objeto directamente en una cadena de comillas dobles?

Respuesta

107

Cuando se delimita un nombre de variable en una cadena entre comillas dobles que será reemplazado por el valor de esa variable:

$foo = 2 
"$foo" 

convierte

"2" 

Si no quieren que usted tiene que use comillas simples:

$foo = 2 
'$foo' 

Sin embargo, si desea acceder a las propiedades, o use índices en var iables en una cadena entre comillas dobles, que tienen que encerrar 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 se expande 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 encerrarlos en el operador de subexpresión $(), lo que hace que la expresión interna se evalúe y se incruste en la cadena.

+0

Esto funciona, pero me pregunto si también podría concatenar cadenas, ya que estaba tratando de evitar en primer lugar – ozzy432836

+2

@ ozzy432836 Puede, por supuesto. O use cadenas de formato. Por lo general, no importa y se reduce a las preferencias personales. – Joey

12

@Joey tiene la respuesta correcta, pero sólo para añadir un poco más de por qué necesita forzar la evaluación con $():

Su código de ejemplo contiene una ambigüedad que apunta a por qué los fabricantes de PowerShell pueden tener elegido para limitar la expansión a meras referencias de variables y no admite el acceso a las propiedades también (como un aparte: la expansión de cadenas se realiza llamando al método ToString() en el objeto, lo que puede explicar algunos resultados "impares").

Su ejemplo que figura al final de la línea de comandos:

...\$LogFileName.ldf 

Si las propiedades de los objetos se ampliaron de forma predeterminada, lo anterior podría resolver a

...\ 

ya que el objeto referenciado por $LogFileName no tendría una propiedad llamada ldf, $null (o una cadena vacía) se sustituiría por la variable.

+1

Buen hallazgo. De hecho, no estaba completamente seguro de cuál era su problema, pero tratar de acceder a las propiedades desde dentro de una cadena olía mal :) – Joey

+0

¡Gracias chicos! ¿Por qué crees que acceder a las propiedades en una cadena huele mal? ¿Cómo sugeriría que se haga? –

+0

hombre de las cavernas: Simplemente fue algo que me llamó la atención por ser una posible causa de error. Ciertamente puedes hacerlo y no es nada extraño, pero al omitir el operador $() grita "Fail" porque no puede funcionar. Por lo tanto, mi respuesta fue solo una suposición educada sobre cuál podría ser su poblema, utilizando la primera falla posible porque pude ver. Disculpe si parecía, pero ese comentario no se refería a ninguna práctica recomendada más o menos. – Joey

6

@Joey tiene una buena respuesta. Hay otra manera con un aspecto más .NET con una cadena.Formato equivalente, lo prefiero cuando se accede a las propiedades de los objetos:

cosas acerca de un coche:

$properties = @{ 'color'='red'; 'type'='sedan'; 'package'='fully loaded'; } 

crear 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 
+0

Sí, es más legible, especialmente si está acostumbrado al código .net. ¡Gracias! :) –

+2

Hay que tener cuidado con la primera vez que utilizas cadenas de formato, que a menudo necesitarás encerrar la expresión en parens. No puede, por ejemplo, simplemente escribir 'write-host 'foo = {0}" -f $ foo' ya que PowerShell tratará '-f' como el parámetro' ForegroundColor' para 'write-host'. En este ejemplo, necesitarías 'write-host (" foo = {0} "-f $ foo)'. Este es el comportamiento estándar de PowerShell, pero vale la pena señalarlo. – andyb

3

Nota sobre la documentación: Get-Help about_Quoting_Rules cubre la interpolación de cadenas, pero, a partir de PSv5, no en profundidad.

Para complementar Joey's helpful answer con un resumen pragmática de cadena expansión de PowerShell (interpolación de cadenas en entre comillas dobles cadenas, incluso en comillas dobles here-strings):

  • Sólo las referencias como $foo, $global:foo (o $script:foo, ...) y $env:PATH (variables de entorno) se reconocen cuando directamente incrustado en una cadena "..." - es decir, solo se amplía la referencia de variable , independientemente de lo que sigue.

    • Para disambiguate un nombre de variable de caracteres subsiguientes en la cadena, encerrarlo entre { y }; por ejemplo, ${foo}.
      Esto es especialmente importante si el nombre de la variable es seguida por una :, ya que de lo contrario sería PowerShell considerar todo entre el $ y la : un alcance especificador, por lo general haciendo que la interpolación para fallan; por ejemplo, "$HOME: where the heart is." se rompe, pero "${HOME}: where the heart is." funciona según lo previsto.
      (Alternativamente, ` -cape el :: "$HOME`: where the heart is.").

    • para tratar una $ o una " como literal, prefijo con Char escape. ` (a backtick); p.ej.:
      "`$HOME's value: `"$HOME`""

  • Para cualquier otra cosa, incluyendo el uso de subíndices de matriz y acceder propiedades de una variable de objeto, debe encerrar la expresión en $(...), el operador subexpresión (por ejemplo, "PS version: $($PSVersionTable.PSVersion)" o "1st el.: $($someArray[0])")

    • El uso de $(...) incluso le permite incrustar la salida de líneas de comando completas en cadenas de comillas dobles (por ejemplo, "Today is $((Get-Date).ToString('d')).").
  • resultados de interpolación no necesariamente tienen el mismo aspecto como el formato de salida predeterminado (lo que vería si imprimió la variable/subexpresión directamente a la consola, por ejemplo, que implica el formateador predeterminado; ver Get-Help about_format.ps1xml):

    • Colecciones, incluyendo matrices, se convierten en cadenas por la colocación de un único espacio entre las representaciones de cadena o f los elementos (de forma predeterminada; un separador diferente se puede especificar mediante el establecimiento de $OFS) Por ejemplo, "array: $(@(1, 2, 3))" produce array: 1 2 3

    • Las instancias de cualquier otro tipo (incluyendo elementos de colecciones que no son ellos mismos colecciones) están stringified por ya sea llamando a la IFormattable.ToString() método con el cultura invariante, si el tipo de la instancia soporta la interfaz IFormttable[1], o llamando .psobject.ToString(), que en la mayoría de los casos simplemente invoca el tipo de .NET subyacente .ToString() método[2], que puede dar o no una representación significativa: a menos que un tipo (no primitivo) haya anulado específicamente el método .ToString(), todo lo que ' ll get es el nombre de tipo completo (por ejemplo, "hashtable: $(@{ key = 'value' })" cede hashtable: System.Collections.Hashtable).

    • Para obtener el mismo resultado que en la consola de, utilice una subexpresión y tubería para Out-String y aplicar .Trim() para eliminar cualquier líneas vacías anterior y posterior, si se desea; p.ej.,
      "hashtable:`n$((@{ key = 'value' } | Out-String).Trim())" rendimientos:

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

[1] Este comportamiento quizás sorprendente significa que, para los tipos que apoyan representaciones de cultivo sensibles, $obj.ToString() produce una actual -cultura -representación adecuada, mientras que "$obj" (interpolación de cadenas) siempre resulta en invariante en cultura representación - ver this answer mío.

[2] excepción notable:
String-expansión de un [pscustomobject] resultados en un hashtable- como representación a través de .psobject.ToString() (explicado here), mientras que llamar .ToString() produce directamente la cadena vacía.

Cuestiones relacionadas