web-dev-qa-db-fra.com

Comment pouvez-vous utiliser la propriété d'un objet dans une chaîne entre guillemets doubles?

J'ai le code suivant:

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

Lorsque j'essaie d'utiliser l'une des propriétés d'une commande d'exécution de chaîne:

& "$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'"

Il essaie simplement d'utiliser la valeur de $DatabaseSettings plutôt que la valeur de $DatabaseSettings[0].DatabaseName, qui n'est pas valide.
Ma solution consiste à le copier dans une nouvelle variable.

Comment accéder directement à la propriété de l'objet dans une chaîne entre guillemets doubles?

80
caveman_dick

Lorsque vous placez un nom de variable dans une chaîne entre guillemets, il sera remplacé par la valeur de cette variable:

$foo = 2
"$foo"

devient

"2"

Si vous ne voulez pas que vous ayez à utiliser des guillemets simples:

$foo = 2
'$foo'

Cependant, si vous souhaitez accéder aux propriétés ou utiliser des index sur des variables dans une chaîne entre guillemets doubles, vous devez placer cette sous-expression dans $():

$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 ne développe les variables que dans ces cas, rien de plus. Pour forcer l'évaluation d'expressions plus complexes, y compris des index, des propriétés ou même des calculs complets, vous devez les inclure dans l'opérateur de sous-expression $( ), ce qui fait que l'expression à l'intérieur est évaluée et incorporée dans la chaîne.

139
Joey

@Joey a la bonne réponse, mais juste pour ajouter un peu plus pourquoi vous devez forcer l'évaluation avec $():

Votre exemple de code contient une ambiguïté qui explique pourquoi les fabricants de PowerShell ont peut-être choisi de limiter l'expansion à de simples références de variables et de ne pas prendre également en charge l'accès aux propriétés (en tant que côté: l'expansion de chaîne se fait en appelant la fonction ToString() méthode sur l'objet, ce qui peut expliquer certains résultats "bizarres").

Votre exemple contenu à la toute fin de la ligne de commande:

...\$LogFileName.ldf

Si les propriétés des objets étaient développées par défaut, ce qui précède se résoudrait en

...\

puisque l'objet référencé par $LogFileName n'aurait pas de propriété appelée ldf, $null (ou une chaîne vide) remplacerait la variable.

14
Steven Murawski

@Joey a une bonne réponse. Il y a une autre façon avec un look plus .NET avec un équivalent String.Format, je le préfère lors de l'accès aux propriétés sur les objets:

À propos d'une voiture:

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

Créez un objet:

$car = New-Object -typename psobject -Property $properties

Interpoler une chaîne:

"The {0} car is a Nice {1} that is {2}" -f $car.color, $car.type, $car.package

Les sorties:

# The red car is a Nice sedan that is fully loaded
9
loonison101

Note de documentation: Get-Help about_Quoting_Rules couvre l'interpolation de chaînes, mais, à partir de PSv5, pas en profondeur.

Pour compléter réponse utile de Joey avec un résumé pragmatique de PowerShell string expansion (interpolation de chaîne dans entre guillemets chaînes, y compris entre guillemets ici-chaînes ):

  • Seules les références telles que $foo, $global:foo (Ou $script:foo, ...) et $env:PATH (variables d'environnement) sont reconnues lorsque directement incorporé dans une chaîne "..." - c'est-à-dire que seule la référence de variable elle-même est étendu, indépendamment de ce qui suit.

    • Pour lever l'ambiguïté d'un nom de variable à partir des caractères suivants de la chaîne, le placer entre { Et }; par exemple, ${foo}.
      Ceci est particulièrement important si le nom de la variable est suivi d'un :, Car PowerShell considérerait autrement tout entre le $ Et le : A scope spécificateur, provoquant généralement l'interpolation échec; Par exemple, "$HOME: where the heart is." se casse, mais "${HOME}: where the heart is." fonctionne comme prévu.
      (Alternativement, ` - échapper à :: "$HOME`: where the heart is.").

    • Pour traiter un $ Ou un " Comme un littéral, préfixez-le avec caractère d'échappement. ` (a backtick); par exemple.:
      "`$HOME's value: `"$HOME`""

  • Pour toute autre chose, y compris l'utilisation des indices de tableau et l'accès aux propriétés d'une variable objet , vous devez mettre l'expression entre $(...), l'opérateur sous-expression (par exemple, "PS version: $($PSVersionTable.PSVersion)" ou "1st el.: $($someArray[0])")

    • L'utilisation de $(...) vous permet même d'incorporer la sortie de lignes de commande entières dans des chaînes entre guillemets doubles (par exemple, "Today is $((Get-Date).ToString('d')).").
  • Les résultats d'interpolation ne ressemblent pas nécessairement au format de sortie par défaut (ce que vous verriez si vous imprimiez la variable/sous-expression directement sur la console, par exemple, qui implique le formateur par défaut; voir Get-Help about_format.ps1xml ):

    • Les collections , y compris les tableaux, sont converties en chaînes en en plaçant un espace unique entre les représentations de chaîne des éléments (par défaut; un séparateur différent peut être spécifié en définissant $OFS) Par exemple, "array: $(@(1, 2, 3))" donne array: 1 2 3

    • Les instances de tout autre type (y compris les éléments de collections qui ne sont pas elles-mêmes des collections) sont stringifiées par oui appel de la méthode IFormattable.ToString() avec la culture invariante, si le type de l'instance prend en charge le IFormattable interface[1], ou en appelant .psobject.ToString(), qui dans la plupart des cas appelle simplement la méthode .ToString() du type .NET sous-jacent [2], qui peut ou non donner une représentation significative: à moins qu'un type (non primitif) ait spécifiquement outrepassé la méthode .ToString(), tout ce que vous obtiendrez sera le complet nom du type = (par exemple, "hashtable: $(@{ key = 'value' })" donne hashtable: System.Collections.Hashtable).

    • Pour obtenir la même sortie que dans la console , utilisez une sous-expression et un tuyau vers Out-String et appliquez .Trim() pour supprimer les lignes vides de début et de fin, si vous le souhaitez; par exemple.,
      "hashtable:`n$((@{ key = 'value' } | Out-String).Trim())" donne:

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

[1] Ce comportement peut-être surprenant signifie que, pour les types qui prennent en charge les représentations sensibles à la culture, $obj.ToString() donne une actuelle - représentation appropriée à la culture, tandis que "$obj" (interpolation de chaîne) donne toujours une représentation invariante de la culture - voir cette réponse à moi.

[2] Remplacements notables:
* La chaîne de caractères précédemment discutée de collections (liste d'éléments séparés par des espaces plutôt que quelque chose comme System.Object[]).
* La table de hachage - comme représentation des instances de [pscustomobject] (Expliquée ici ) plutôt que la chaîne vide.

6
mklement0