Wie können Sie die Eigenschaft eines Objekts in einer Zeichenfolge in doppelten Anführungszeichen verwenden?

96

Ich habe folgenden Code:

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

Wenn ich versuche, eine der Eigenschaften in einem String-Ausführungsbefehl zu verwenden:

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

Es wird versucht, nur den Wert von $DatabaseSettingsanstelle des Werts von zu verwenden $DatabaseSettings[0].DatabaseName, was nicht gültig ist.
Meine Problemumgehung besteht darin, es in eine neue Variable kopieren zu lassen.

Wie kann ich direkt in einer Zeichenfolge in doppelten Anführungszeichen auf die Objekteigenschaft zugreifen?

caveman_dick
quelle

Antworten:

163

Wenn Sie einen Variablennamen in eine Zeichenfolge in doppelten Anführungszeichen einfügen, wird dieser durch den Wert dieser Variablen ersetzt:

$foo = 2
"$foo"

wird

"2"

Wenn Sie das nicht möchten, müssen Sie einfache Anführungszeichen verwenden:

$foo = 2
'$foo'

Wenn Sie jedoch auf Eigenschaften zugreifen oder Indizes für Variablen in einer Zeichenfolge in doppelten Anführungszeichen verwenden möchten, müssen Sie diesen Unterausdruck in Folgendes einschließ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 erweitert Variablen nur in diesen Fällen, nicht mehr. Um die Auswertung komplexerer Ausdrücke, einschließlich Indizes, Eigenschaften oder sogar vollständiger Berechnungen, zu erzwingen, müssen Sie diese in den Unterausdrucksoperator einschließen $( ), wodurch der Ausdruck im Inneren ausgewertet und in die Zeichenfolge eingebettet wird.

Joey
quelle
Dies funktioniert, aber ich frage mich, ob ich genauso gut Strings verketten kann, wie ich es zu vermeiden versuchte
ozzy432836
2
@ ozzy432836 Das kannst du natürlich. Oder verwenden Sie Formatzeichenfolgen. Es spielt normalerweise keine Rolle und hängt von den persönlichen Vorlieben ab.
Joey
15

@Joey hat die richtige Antwort, aber nur um ein bisschen mehr hinzuzufügen, warum Sie die Bewertung erzwingen müssen mit $():

Ihr Beispielcode enthält eine Mehrdeutigkeit, die darauf hinweist, warum die Hersteller von PowerShell möglicherweise die Erweiterung auf bloße Variablenreferenzen beschränkt haben und den Zugriff auf Eigenschaften nicht unterstützen (abgesehen davon: Die Zeichenfolgenerweiterung erfolgt durch Aufrufen der ToString()Methode für das Objekt kann einige "ungerade" Ergebnisse erklären).

Ihr Beispiel ganz am Ende der Befehlszeile:

...\$LogFileName.ldf

Wenn die Eigenschaften von Objekten standardmäßig erweitert würden, würde das oben Gesagte in aufgelöst

...\

da das durch referenzierte Objekt $LogFileNamenicht eine Eigenschaft namens hat ldf, $null(oder eine leere Zeichenkette) würde für die Variable ersetzt werden.

Steven Murawski
quelle
1
Schöner Fund. Ich war mir eigentlich nicht ganz sicher, was sein Problem war, aber der Versuch, über einen String auf Eigenschaften zuzugreifen, roch schlecht :)
Joey
Danke Leute! Johannes, warum riecht es Ihrer Meinung nach schlecht, auf Eigenschaften in einer Zeichenfolge zuzugreifen? Wie würde vorschlagen, dass es getan wird?
caveman_dick
Höhlenmensch: Es war nur eine Sache, die mir als mögliche Fehlerursache aufgefallen ist. Sie können es sicherlich tun und es ist nichts Seltsames, aber wenn Sie den Operator $ () weglassen, schreit es "Fail", weil es nicht funktionieren kann. Daher war meine Antwort nur eine fundierte Vermutung darüber, was Ihr Problem sein könnte, unter Verwendung der ersten möglichen Fehlerursache, die ich sehen konnte. Es tut mir leid, wenn es so aussah, aber dieser Kommentar bezog sich nicht auf eine bewährte Methode oder so.
Joey
Gut, dass du mir dort Sorgen gemacht hast! ;) Als ich anfing, Powershell zu verwenden, fand ich die Variable in einem String etwas seltsam, aber es ist ziemlich praktisch. Ich habe es einfach nicht geschafft, einen endgültigen Ort zu finden, an dem ich nach Syntaxhilfe suchen kann.
caveman_dick
9

@ Joey hat eine gute Antwort. Es gibt eine andere Möglichkeit mit einem .NET-Look mit einem String.Format-Äquivalent. Ich bevorzuge es beim Zugriff auf Eigenschaften von Objekten:

Dinge über ein Auto:

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

Erstellen Sie ein Objekt:

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

Interpoliere einen String:

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

Ausgänge:

# The red car is a nice sedan that is fully loaded
loonison101
quelle
Ja, das ist besser lesbar, besonders wenn Sie an .net-Code gewöhnt sind. Vielen Dank! :)
caveman_dick
3
Es gibt ein Problem, auf das Sie achten müssen, wenn Sie zum ersten Mal Formatzeichenfolgen verwenden. Dies bedeutet, dass Sie den Ausdruck häufig in Parens einschließen müssen. Sie können beispielsweise nicht einfach schreiben, write-host "foo = {0}" -f $fooda PowerShell -fals ForegroundColorParameter für behandelt wird write-host. In diesem Beispiel müssten Sie write-host ( "foo = {0}" -f $foo ). Dies ist das Standardverhalten von PowerShell, aber erwähnenswert.
Andyb
Um pedantisch zu sein, ist das obige Beispiel nicht "String-Interpolation", sondern "zusammengesetzte Formatierung". Da Powershell untrennbar mit .NET verbunden ist, für das C # und VB.NET die primären Programmiersprachen sind, sollten die Begriffe "Zeichenfolgeninterpolation" und "interpolierte Zeichenfolge" (und ähnliche Elemente) nur auf die neuen $ vorangestellten Zeichenfolgen angewendet werden, die in diesen gefunden werden Sprachen (C # 6 und VB.NET 14). In diesen Zeichenfolgen werden Parameterausdrücke anstelle von numerischen Parameterreferenzen direkt in die Zeichenfolge eingebettet, wobei die tatsächlichen Ausdrücke dann Argumente für sind String.Format.
Uber Kluger
@andyb, bezüglich der Formatzeichenfolge "Fallstricke". Dies ist eigentlich der Unterschied zwischen dem Ausdrucks- und dem Argumentmodus (siehe about_Parsing ). Befehle werden in Token analysiert (Gruppen von Zeichen, die eine aussagekräftige Zeichenfolge bilden). Das Leerzeichen trennt Token, die zusammengeführt werden würden, ansonsten aber ignoriert werden. Wenn das erste Token des Befehls eine Zahl, eine Variable, ein Anweisungsschlüsselwort (if, while usw.), ein unärer Operator, {usw. usw. ist, ist der Analysemodus Expressionanders Argument(bis zu einem Befehlsabschluss).
Uber Kluger
8

Hinweis zur Dokumentation: Get-Help about_Quoting_Rules Deckt die String-Interpolation ab, ab PSv5 jedoch nicht mehr ausführlich.

Um Joeys hilfreiche Antwort mit einer pragmatischen Zusammenfassung von PowerShell zu ergänzen - String Erweiterung (string Interpolation in doppelten Anführungszeichen Strings ("...")einschließlich in doppelten Anführungszeichen hier-Strings ):

  • Nur Referenzen wie $foo, $global:foo(oder$script:foo , ...) und$env:PATH (Umgebungsvariablen) werden erkannt, wenn sie direkt in eine "..."Zeichenfolge eingebettet sind - das heißt, nur die Variablenreferenz selbst wird erweitert, unabhängig davon, was folgt.

    • Um einen Variablennamen von nachfolgenden Zeichen in der Zeichenfolge zu unterscheiden, schließen Sie ihn in {und ein} . zB , ${foo}.
      Dies ist besonders wichtig , wenn der Variablenname gefolgt von einem wird :, wie Powershell sonst alles zwischen dem in Betracht ziehen würde $und :ein Umfang Spezifizierer, in der Regel die Interpolation verursacht zu scheitern ; zB "$HOME: where the heart is."bricht, "${HOME}: where the heart is."funktioniert aber wie vorgesehen.
      (Alternativ `-escape der :: "$HOME`: where the heart is.").

    • Um a $oder a "als Literal zu behandeln , stellen Sie ihm ein Escapezeichen voran. `(ein Backtick ); z.B:
      "`$HOME's value: `"$HOME`""

  • Für alles andere, einschließlich der Verwendung von Array-Indizes und des Zugriffs auf die Eigenschaften einer Objektvariablen , müssen Sie den Ausdruck in$(...) den Unterausdrucksoperator (z. B. "PS version: $($PSVersionTable.PSVersion)"oder "1st el.: $($someArray[0])") einschließen.

    • Wenn Sie $(...)sogar verwenden, können Sie die Ausgabe ganzer Befehlszeilen in Zeichenfolgen in doppelten Anführungszeichen (z "Today is $((Get-Date).ToString('d')).". B. ) einbetten .
  • Interpolationsergebnisse sehen nicht unbedingt genauso aus wie das Standardausgabeformat (was Sie sehen würden, wenn Sie die Variable / den Unterausdruck beispielsweise direkt auf die Konsole drucken würden, was den Standardformatierer betrifft; siehe Get-Help about_format.ps1xml):

    • Sammlungen , einschließlich Arrays, werden durch Platzieren eines einzelnen Leerzeichens in Zeichenfolgen konvertiert zwischen den Stringdarstellungen der Elemente (Standard, ein anderes Trenn kann durch Einstellung festgelegt werden $OFS) Eg, "array: $(@(1, 2, 3))"Ausbeutenarray: 1 2 3

    • Instanzen von jedem anderen Typ (einschließlich der Elemente der Sammlung, die selbst nicht Sammlungen sind) werden Zeichenfolge von entweder der anrufenden IFormattable.ToString()Methode mit der invarianten Kultur , wenn der Typ der Instanz die Abstützungen IFormattableSchnittstelle [1] , oder durch den Aufruf .psobject.ToString(), die in den meisten Fällen einfach Invokes die .ToString()Methode des zugrunde liegenden .NET-Typs [2] , die möglicherweise eine aussagekräftige Darstellung liefert oder nicht: Sofern ein (nicht primitiver) Typ die .ToString()Methode nicht speziell überschrieben hat , erhalten Sie lediglich den vollständigen Typnamen (z . B. "hashtable: $(@{ key = 'value' })"Ausbeuten hashtable: System.Collections.Hashtable).

    • Um die gleiche Ausgabe wie in der Konsole zu erhalten , verwenden Sie einen Unterausdruck und eine Pipe fürOut-String und wenden Sie diese .Trim()an, um gegebenenfalls führende und nachfolgende leere Zeilen zu entfernen. z.B,
      "hashtable:`n$((@{ key = 'value' } | Out-String).Trim())" ergibt:

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

[1] Dieses vielleicht überraschende Verhalten bedeutet, dass für Typen, die kultursensitive Darstellungen unterstützen, $obj.ToString()ein Strom entsteht kultursensitive kulturgerechte Darstellung erhalten wird, während "$obj"(String-Interpolation) immer zu a führt kulturinvarianten Darstellung führt - siehe meine Antwort .

[2] Bemerkenswerte Überschreibungen:
* Die zuvor diskutierte Stringifizierung von Sammlungen (durch Leerzeichen getrennte Liste von Elementen anstelle von so etwas System.Object[]).
* Die hashtable- wie Darstellung von [pscustomobject]Instanzen (erklärt hier ) , anstatt der leeren String .

mklement0
quelle