Functieretourwaarde in PowerShell

Ik heb een PowerShell-functie ontwikkeld die een aantal acties uitvoert met betrekking tot het inrichten van SharePoint Teamsites. Uiteindelijk wil ik dat de functie de URL van de ingerichte site als een tekenreeks retourneert, dus aan het einde van mijn functie heb ik de volgende code:

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

De code die deze functie aanroept ziet er als volgt uit:

$returnURL = MyFunction -param 1 ...

Dus ik verwacht een string, maar dat is niet zo. In plaats daarvan is het een object van het type System.Management.Automation.PSMethod. Waarom retourneert het dat type in plaats van een String-type?


Antwoord 1, autoriteit 100%

PowerShell heeft echt maffe return-semantiek – tenminste gezien vanuit een meer traditioneel programmeerperspectief. Er zijn twee hoofdideeën om je hoofd rond te wikkelen:

  • Alle uitvoer wordt vastgelegd en geretourneerd
  • Het return-trefwoord geeft eigenlijk alleen maar een logisch vertrekpunt aan

Dus de volgende twee scriptblokken zullen in feite precies hetzelfde doen:

$a = "Hello, World"
return $a

 

$a = "Hello, World"
$a
return

De $a variabele in het tweede voorbeeld blijft als uitvoer in de pijplijn en, zoals vermeld, wordt alle uitvoer geretourneerd. In het tweede voorbeeld zou je de return zelfs helemaal kunnen weglaten en zou je hetzelfde gedrag krijgen (de return zou impliciet zijn omdat de functie natuurlijk wordt voltooid en afgesloten).

Zonder je functiedefinitie kan ik niet zeggen waarom je een PSMethod-object krijgt. Ik vermoed dat je een paar regels verderop waarschijnlijk iets hebt dat niet wordt vastgelegd en op de uitvoerpijplijn wordt geplaatst.

Het is ook vermeldenswaard dat u waarschijnlijk die puntkomma’s niet nodig heeft – tenzij u meerdere uitdrukkingen op een enkele regel nest.

Je kunt meer lezen over de retoursemantiek op de about_Return pagina op TechNet, of door de opdracht help return op te roepen vanuit PowerShell zelf.


Antwoord 2, autoriteit 20%

Dit deel van PowerShell is waarschijnlijk het meest domme aspect. Elke externe output die tijdens een functie wordt gegenereerd, vervuilt het resultaat. Soms is er geen output, en onder sommige omstandigheden is er een andere ongeplande output, naast je geplande retourwaarde.

Dus ik verwijder de toewijzing van de oorspronkelijke functieaanroep, zodat de uitvoer op het scherm komt, en stap er dan doorheen totdat iets dat ik niet had gepland in het foutopsporingsvenster verschijnt (met behulp van de PowerShell ISE).

Zelfs dingen als het reserveren van variabelen in outer scopes veroorzaken uitvoer, zoals [boolean]$isEnabled die irritant een False uitspuugt, tenzij je het [boolean]$isEnabled = $false.

Een andere goede is $someCollection.Add("thing") die de nieuwe collectietelling uitspuugt.


Antwoord 3, autoriteit 17%

Met PowerShell 5 hebben we nu de mogelijkheid om klassen te maken. Verander je functie in een klasse, en return zal alleen het object teruggeven dat er direct aan voorafgaat. Hier is een heel eenvoudig voorbeeld.

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

Als dit een functie was, zou de verwachte uitvoer zijn

Hello World
808979

maar als klasse is het enige dat wordt geretourneerd het gehele getal 808979. Een klasse is een soort garantie dat het alleen het gedeclareerde of ongeldige type retourneert.


Antwoord 4, autoriteit 11%

Als tijdelijke oplossing heb ik het laatste object in de array geretourneerd dat u van de functie terugkrijgt… Het is geen geweldige oplossing, maar het is beter dan niets:

someFunction {
   $a = "hello"
   "Function is running"
   return $a
}
$b = someFunction
$b = $b[($b.count - 1)]  # Or
$b = $b[-1]              # Simpler

Al met al zou een meer eenregelige manier om hetzelfde te schrijven kunnen zijn:

$b = (someFunction $someParameter $andAnotherOne)[-1]

Antwoord 5, autoriteit 2%

Ik geef een eenvoudig Hashtable-object door met een enkel resultaatlid om de gekte van terugkeer te voorkomen, omdat ik ook naar de console wil uitvoeren. Het werkt via pass-by-referentie.

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

Je kunt echt voorbeeldgebruik zien in mijn GitHub-project unpackTunes.


Antwoord 6, autoriteit 2%

De bestaande antwoorden zijn correct, maar soms retourneert u niet echt iets expliciet met een Write-Output of een return, maar toch zit er een mysterieuze waarde in de functie resultaten. Dit kan de uitvoer zijn van een ingebouwde functie zoals New-Item

PS C:\temp> function ContrivedFolderMakerFunction {
>>    $folderName = [DateTime]::Now.ToFileTime()
>>    $folderPath = Join-Path -Path . -ChildPath $folderName
>>    New-Item -Path $folderPath -ItemType Directory
>>    return $true
>> }
PS C:\temp> $result = ContrivedFolderMakerFunction
PS C:\temp> $result
    Directory: C:\temp
Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----         2/9/2020   4:32 PM                132257575335253136
True

Al die extra ruis van het maken van mappen wordt verzameld en uitgezonden in de uitvoer. De makkelijke manier om dit te verminderen is door | Out-Null aan het einde van de New-Item-instructie, of u kunt het resultaat aan een variabele toewijzen en die variabele gewoon niet gebruiken. Het zou er zo uitzien…

PS C:\temp> function ContrivedFolderMakerFunction {
>>    $folderName = [DateTime]::Now.ToFileTime()
>>    $folderPath = Join-Path -Path . -ChildPath $folderName
>>    New-Item -Path $folderPath -ItemType Directory | Out-Null
>>    # -or-
>>    $throwaway = New-Item -Path $folderPath -ItemType Directory 
>>    return $true
>> }
PS C:\temp> $result = ContrivedFolderMakerFunction
PS C:\temp> $result
True

New-Item is waarschijnlijk de bekendste hiervan, maar andere bevatten alle StringBuilder.Append*()-methoden, evenals de SqlDataAdapter.Fill() methode.


Antwoord 7, autoriteit 2%

Het is moeilijk te zeggen zonder naar de code te kijken. Zorg ervoor dat uw functie niet meer dan één object retourneert en dat u alle resultaten van andere aanroepen vastlegt. Waar krijg je voor:

@($returnURL).count

Hoe dan ook, twee suggesties:

Cast het object naar string:

...
return [string]$rs

Of plaats het gewoon tussen dubbele aanhalingstekens, hetzelfde als hierboven, maar korter om te typen:

...
return "$rs"

Antwoord 8

Luke’s beschrijving van de functieresultaten in deze scenario’s lijkt gelijk hebben. Ik wil alleen de hoofdoorzaak begrijpen en het PowerShell-productteam zou iets aan het gedrag doen. Het schijnt heel gewoon te zijn en heeft me te veel tijd gekost om fouten op te sporen.

Om dit probleem te omzeilen heb ik globale variabelen gebruikt in plaats van de waarde uit de functie-aanroep terug te geven en te gebruiken.

Hier is nog een vraag over het gebruik van globale variabelen:
Een globale PowerShell-variabele van een functie waarbij de naam van de globale variabele een variabele is die wordt doorgegeven aan de functie


Antwoord 9

Het volgende retourneert eenvoudig 4 als antwoord. Als je de add-expressies voor strings vervangt, wordt de eerste string geretourneerd.

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

Dit kan ook voor een array. Het onderstaande voorbeeld retourneert “Tekst 2”

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

Houd er rekening mee dat je de functie onder de functie zelf moet aanroepen. Anders zal het de eerste keer dat het wordt uitgevoerd een fout retourneren dat het niet weet wat “StartingMain” is.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

fourteen − ten =

Other episodes