it-swarm.com.de

Funktionsrückgabewert in PowerShell

Ich habe eine PowerShell-Funktion entwickelt, die eine Reihe von Aktionen ausführt, die die Bereitstellung von SharePoint Teamwebsites umfassen. Letztendlich möchte ich, dass die Funktion die URL der bereitgestellten Site als String zurückgibt, sodass ich am Ende meiner Funktion den folgenden Code habe:

$rs = $url.ToString();
return $rs;

Der Code, der diese Funktion aufruft, sieht folgendermaßen aus:

$returnURL = MyFunction -param 1 ...

Ich erwarte also einen String, aber das ist es nicht. Stattdessen ist es ein Objekt vom Typ System.Management.Automation.PSMethod. Warum wird dieser Typ anstelle eines String-Typs zurückgegeben?

158
ChiliYago

PowerShell hat eine verrückte Rückgabesemantik - zumindest aus einer traditionelleren Programmierperspektive. Es gibt zwei Hauptideen, um Ihren Kopf herumzuwickeln:

  • Alle Ausgaben werden erfasst und zurückgegeben
  • Das Schlüsselwort return gibt wirklich nur einen logischen Austrittspunkt an

Die folgenden beiden Skriptblöcke bewirken also genau dasselbe:

$a = "Hello, World"
return $a

$a = "Hello, World"
$a
return

Die Variable $ a im zweiten Beispiel wird als Ausgabe in der Pipeline belassen und, wie erwähnt, die gesamte Ausgabe zurückgegeben. Tatsächlich könnten Sie im zweiten Beispiel die Rückgabe vollständig weglassen und Sie würden das gleiche Verhalten erhalten (die Rückgabe würde impliziert, da die Funktion natürlich abgeschlossen und beendet wird).

Ohne mehr von Ihrer Funktionsdefinition kann ich nicht sagen, warum Sie ein PSMethod-Objekt erhalten. Ich vermute, dass Sie wahrscheinlich ein paar Zeilen haben, die nicht erfasst und in der Ausgabe-Pipeline platziert werden.

Es ist auch erwähnenswert, dass Sie wahrscheinlich diese Semikolons nicht benötigen - es sei denn, Sie verschachteln mehrere Ausdrücke in einer einzigen Zeile.

Weitere Informationen zur Rückgabesemantik finden Sie auf der Seite about_Return im TechNet oder durch Aufrufen des Befehls help return in PowerShell.

246
Goyuix

Dieser Teil von PowerShell ist wahrscheinlich der dümmste Aspekt. Jede externe Ausgabe, die während einer Funktion generiert wird, verschmutzt das Ergebnis. Manchmal gibt es keine Ausgabe und unter bestimmten Umständen gibt es zusätzlich zu Ihrem geplanten Rückgabewert eine andere ungeplante Ausgabe.

Ich entferne also die Zuweisung aus dem ursprünglichen Funktionsaufruf, sodass die Ausgabe auf dem Bildschirm angezeigt wird, und gehe dann so lange vor, bis im Debugger-Fenster (mithilfe der PS ISE) etwas angezeigt wird, das ich nicht geplant habe.

Sogar Dinge wie das Reservieren von Variablen in äußeren Bereichen führen zu Ausgaben wie [boolean]$isEnabled, die lästigerweise einen False-Out ausspucken, es sei denn, Sie machen es [boolean]$isEnabled = $false.

Ein anderes gutes ist $someCollection.Add("thing"), das die Anzahl der neuen Sammlungen angibt.

45
Luke Puplett

Mit PowerShell 5 können wir jetzt Klassen erstellen. Wenn Sie Ihre Funktion in eine Klasse ändern, können Sie sie nicht nur eingeben, sondern mit return NUR das unmittelbar davor stehende Objekt zurückgeben. Hier ist ein wirklich einfaches Beispiel.

class test_class{
    [int]return_what() {
        Write-Output "Hello World"
        return 808979
    }
}
$tc = New-Object -TypeName test_class
$tc.return_what()

Wenn dies eine Funktion wäre, wäre die erwartete Ausgabe

Hello World
808979

als Klasse wird jedoch nur die INT 808979 zurückgegeben. Eine Klasse ist gewissermaßen eine Garantie dafür, dass sie nur den deklarierten oder ungültigen Typ zurückgibt.

33
DaSmokeDog

Als Workaround habe ich das letzte Objekt im Array zurückgegeben, das Sie von der Funktion zurückerhalten ... keine großartige Lösung, aber es ist besser als nichts:

someFunction {
   $a = "hello"
   "Function is running"
   return $a
}

$b = someFunction
$b = $b[($b.count - 1)]  # or 
$b = $b[-1]              # simpler
23
Jonesy

Ich übergebe ein einfaches Hashtable-Objekt mit einem einzelnen Ergebniselement, um die Rückgabewahnsinnigkeit zu vermeiden, da ich auch auf der Konsole ausgeben möchte. Es wirkt durch Referenzübergabe.

function sample-loop($returnObj) {
  for($i = 0; $i -lt 10; $i++) {
    write-Host "loop counter: $i"
    $returnObj.result++
  }
}

function main-sample() {
  $countObj = @{ result = 0 }
  sample-loop -returnObj $countObj
  write-Host "_____________"
  write-Host "Total = " ($countObj.result)
}

main-sample

Sie können die reale Beispielnutzung in meinem GitHub-Projekt unpackTunes sehen

5

Es ist schwer zu sagen, ohne den Code zu betrachten. Stellen Sie sicher, dass Ihre Funktion nicht mehr als ein Objekt zurückgibt und dass Sie die Ergebnisse anderer Aufrufe erfassen. Was bekommen Sie für:

@($returnURL).count

Trotzdem zwei Vorschläge:

Das Objekt in eine Zeichenfolge umwandeln:

...
return [string]$rs

Oder schließen Sie es einfach in doppelte Anführungszeichen ein, wie oben, aber kürzer, um Folgendes einzugeben:

...
return "$rs"
4
Shay Levy

Lukes Beschreibung der Funktionsergebnisse in diesen Szenarien scheint richtig zu sein. Ich möchte nur die Hauptursache verstehen, und das PowerShell-Produktteam würde etwas gegen das Verhalten unternehmen, scheint ziemlich häufig zu sein und hat zu viel Debugging-Zeit gekostet.

Um dieses Problem zu umgehen, habe ich globale Variablen verwendet, anstatt den Wert des Funktionsaufrufs zurückzugeben und zu verwenden.

Hier ist ein weiterer Thread zur Verwendung globaler Variablen: Festlegen einer globalen PowerShell-Variablen aus einer Funktion, bei der der Name der globalen Variablen eine an die Funktion übergebene Variable ist

4
Ted B.

Das Folgende gibt einfach 4 als Antwort zurück. Wenn Sie die Add-Ausdrücke für Zeichenfolgen ersetzen, wird die erste Zeichenfolge zurückgegeben.

Function StartingMain {
  $a = 1 + 3
  $b = 2 + 5
  $c = 3 + 7
  Return $a
}
Function StartingEnd($b) {
  Write-Host $b
}
StartingEnd(StartingMain)

Dies kann auch für ein Array erfolgen. Das folgende Beispiel gibt "Text 2" zurück

Function StartingMain {
  $a = ,@("Text 1","Text 2","Text 3")
  Return $a
}
Function StartingEnd($b) {
  Write-Host $b[1]
}
StartingEnd(StartingMain)

Beachten Sie, dass Sie die Funktion unterhalb der Funktion selbst aufrufen müssen. Andernfalls wird beim ersten Ausführen eine Fehlermeldung ausgegeben, die besagt, dass "StartingMain" nicht bekannt ist.

Ich hoffe es hilft.

3
Edwin

Als Alternative zu return würde ich Write-Output vorschlagen.

Write-Output kann Eingaben von der Pipeline entgegennehmen und entweder in der Pipeline weiterleiten oder sie an die Konsole ausgeben, wenn es sich um den letzten Befehl handelt. Write-Output beendet das Skript jedoch nicht wie return, was von Vorteil sein kann, wenn die Ausgabe verwendet/weitergeleitet/etc. Werden soll. in einer Schleife.

0
Alex_P