Come puoi usare la proprietà di un oggetto in una stringa tra virgolette?


96

Ho il codice seguente:

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

Quando provo a utilizzare una delle proprietà in una stringa, esegui il 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'"

Cerca di utilizzare solo il valore di $DatabaseSettingsanziché il valore di $DatabaseSettings[0].DatabaseName, che non è valido.
La mia soluzione è copiarlo in una nuova variabile.

Come posso accedere alla proprietà dell'oggetto direttamente in una stringa tra virgolette?

Risposte:


163

Quando racchiudi un nome di variabile in una stringa tra virgolette, verrà sostituito dal valore di quella variabile:

$foo = 2
"$foo"

diventa

"2"

Se non vuoi che devi usare virgolette singole:

$foo = 2
'$foo'

Tuttavia, se si desidera accedere alle proprietà o utilizzare gli indici sulle variabili in una stringa tra virgolette doppie, è necessario racchiudere tale sottoespressione tra $():

$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 espande le variabili solo in questi casi, niente di più. Per forzare la valutazione di espressioni più complesse, inclusi indici, proprietà o persino calcoli completi, è necessario racchiuderli nell'operatore di sottoespressione $( )che fa sì che l'espressione all'interno venga valutata e incorporata nella stringa.


Funziona ma mi chiedo se posso anche solo concatenare le stringhe come stavo cercando di evitare in primo luogo
ozzy432836

2
@ ozzy432836 Puoi, ovviamente. Oppure usa le stringhe di formato. Di solito non ha molta importanza e dipende dalle preferenze personali.
Joey

15

@Joey ha la risposta corretta, ma solo per aggiungere un po 'di più sul motivo per cui è necessario forzare la valutazione con $():

Il codice di esempio contiene un'ambiguità che indica il motivo per cui i creatori di PowerShell potrebbero aver scelto di limitare l'espansione a semplici riferimenti a variabili e non supportare anche l'accesso alle proprietà (per inciso: l'espansione della stringa viene eseguita chiamando il ToString()metodo sull'oggetto, che può spiegare alcuni risultati "dispari").

Il tuo esempio contenuto alla fine della riga di comando:

...\$LogFileName.ldf

Se le proprietà degli oggetti fossero espanse per impostazione predefinita, quanto sopra si risolverà in

...\

poiché l'oggetto a cui fa riferimento $LogFileNamenon avrebbe una proprietà chiamata ldf, $null(o una stringa vuota) verrebbe sostituita dalla variabile.


1
Bella scoperta. In realtà non ero completamente sicuro di quale fosse il suo problema, ma il tentativo di accedere alle proprietà dall'interno di una stringa aveva un cattivo odore :)
Joey

Grazie ragazzi! Johannes perché pensi che l'accesso alle proprietà in una stringa abbia un cattivo odore? Come suggerirei che sia fatto?
caveman_dick

Cavernicolo: Era solo una cosa che ha catturato la mia attenzione come una potenziale causa di errore. Puoi certamente farlo e non è niente di strano, ma quando ometti l'operatore $ () urla "Fallito" perché non può funzionare. Quindi la mia risposta era solo un'ipotesi plausibile su quale potesse essere il tuo poblem, usando la prima possibile causa di fallimento che potevo vedere. Scusa se sembrava, ma quel commento non si riferiva a nessuna best practice o giù di lì.
Joey

Bene, mi hai fatto preoccupare lì! ;) Quando ho iniziato a usare PowerShell ho pensato che la variabile in una stringa fosse un po 'strana, ma è abbastanza utile. Semplicemente non sono riuscito a trovare un posto definitivo in cui cercare aiuto sulla sintassi.
caveman_dick

9

@ Joey ha una buona risposta. C'è un altro modo con un aspetto più .NET con un equivalente String.Format, lo preferisco quando si accede alle proprietà sugli oggetti:

Cose su una macchina:

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

Crea un oggetto:

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

Interpola una stringa:

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

Uscite:

# The red car is a nice sedan that is fully loaded

Sì, è più leggibile soprattutto se sei abituato al codice .net. Grazie! :)
caveman_dick

3
C'è un trucco a cui prestare attenzione quando usi per la prima volta le stringhe di formato, ovvero che spesso dovrai racchiudere l'espressione tra parentesi. Non è possibile, ad esempio, scrivere semplicemente write-host "foo = {0}" -f $foopoiché PowerShell tratterà -fcome ForegroundColorparametro per write-host. In questo esempio dovresti write-host ( "foo = {0}" -f $foo ). Questo è il comportamento standard di PowerShell, ma vale la pena notare.
andyb

Giusto per essere pedanti, l'esempio sopra non è "interpolazione di stringhe", è "formattazione composita". Poiché Powershell è indissolubilmente legato a .NET per il quale C # e VB.NET sono i linguaggi di programmazione principali, i termini "interpolazione di stringhe" e "stringa interpolata" (e qualsiasi cosa simile) dovrebbero essere applicati solo alle nuove stringhe $ anteposte trovate in quelle linguaggi (C # 6 e VB.NET 14). In queste stringhe, le espressioni dei parametri sono direttamente incorporate nella stringa anziché i riferimenti numerici ai parametri, con le espressioni effettive che vengono quindi utilizzate come argomenti String.Format.
Uber Kluger

@andyb, per quanto riguarda la stringa di formato "gotchas". Questa è effettivamente la differenza tra Expression e Argument mode (vedere about_Parsing ). I comandi vengono analizzati in token (gruppi di caratteri che formano una stringa significativa). Lo spazio separa i token che verrebbero uniti ma altrimenti ignorati. Se il primo token del comando è un numero, una variabile, una parola chiave dell'istruzione (if, while, ecc.), Un operatore unario, {, ecc ... allora la modalità di analisi è Expressiondiversa Argument(fino a un terminatore di comando).
Uber Kluger

8

Nota sulla documentazione: Get-Help about_Quoting_Rulescopre l'interpolazione delle stringhe, ma, a partire da PSv5, non è approfondita.

Per completare risposta utile di Joey con una sintesi pragmatica di PowerShell di espansione di stringa (stringa di interpolazione in doppi apici stringhe ( "..."), anche in doppi apici stringhe here ):

  • Solo i riferimenti come $foo, $global:foo(o $script:foo, ...) e$env:PATH (variabili di ambiente) vengono riconosciuti quando sono incorporati direttamente in una "..."stringa, ovvero solo il riferimento alla variabile stesso viene espanso, indipendentemente da quanto segue.

    • Per disambiguare il nome di una variabile dai caratteri successivi nella stringa, racchiuderlo tra {e} ; es ${foo}.
      Questo è particolarmente importante se il nome della variabile è seguito da un :, come PowerShell altrimenti considerare tutto tra $e :una portata specificatore, tipicamente causando l'interpolazione di sicuro ; ad esempio, si "$HOME: where the heart is."interrompe, ma "${HOME}: where the heart is."funziona come previsto.
      (In alternativa, `-escape la :: "$HOME`: where the heart is.").

    • Per trattare a $o a "come letterale , anteponi il carattere di escape. `(un backtick ); per esempio:
      "`$HOME's value: `"$HOME`""

  • Per qualsiasi altra cosa, incluso l'uso di pedici di array e l'accesso alle proprietà di una variabile oggetto , devi racchiudere l'espressione in$(...) , l' operatore di sottoespressione (ad esempio, "PS version: $($PSVersionTable.PSVersion)"o "1st el.: $($someArray[0])")

    • L'uso $(...)consente anche di incorporare l'output di intere righe di comando in stringhe tra virgolette (ad esempio "Today is $((Get-Date).ToString('d')).").
  • I risultati dell'interpolazione non sembrano necessariamente uguali al formato di output predefinito (cosa vedresti se stampassi la variabile / sottoespressione direttamente sulla console, ad esempio, che coinvolge il formattatore predefinito; vedi Get-Help about_format.ps1xml):

    • Le raccolte , inclusi gli array, vengono convertite in stringhe inserendo un singolo spazio tra le rappresentazioni di stringa degli elementi (per impostazione predefinita; è possibile specificare un separatore diverso impostando $OFS) Ad esempio, "array: $(@(1, 2, 3))"restituiscearray: 1 2 3

    • Le istanze di qualsiasi altro tipo (compresi gli elementi di collezioni che non sono esse stesse collezioni) sono stringata da uno chiamando il IFormattable.ToString()metodo con l' cultura invarianti , se il tipo di istanza supporta l' IFormattableinterfaccia di [1] , o chiamando il numero .psobject.ToString(), che nella maggior parte dei casi semplicemente invoca il .ToString()metodo del tipo .NET sottostante [2] , che può o non può fornire una rappresentazione significativa: a meno che un tipo (non primitivo) non abbia specificatamente sovrascritto il .ToString()metodo, tutto ciò che otterrai è il nome completo del tipo (ad esempio, "hashtable: $(@{ key = 'value' })"produce hashtable: System.Collections.Hashtable).

    • Per ottenere lo stesso output della console , utilizzare una sottoespressione e pipe aOut-String e applicare .Trim()per rimuovere eventuali righe vuote iniziali e finali, se lo si desidera; ad esempio,
      "hashtable:`n$((@{ key = 'value' } | Out-String).Trim())"produce:

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

[1] Questo comportamento forse sorprendente significa che, per i tipi che supportano rappresentazioni sensibili alla cultura, $obj.ToString()produce una rappresentazione appropriata alla cultura corrente , mentre "$obj"(interpolazione di stringhe) si traduce sempre in una rappresentazione invariante alla cultura - vedi questa mia risposta .

[2] Notevoli sostituzioni:
* La stringa di raccolte precedentemente discussa (elenco di elementi separati da spazi piuttosto che qualcosa di simile System.Object[]).
* La rappresentazione simile a una tabella hash delle [pscustomobject]istanze (spiegata qui ) piuttosto che la stringa vuota .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.