PowerShell'de işlev dönüş değeri


188

SharePoint Team sitelerinin sağlanmasını içeren bir dizi eylem gerçekleştiren bir PowerShell işlevi geliştirdim . Sonuçta, fonksiyonumun sonunda aşağıdaki kod var böylece fonksiyonun sağlanan sitenin URL'sini bir String olarak döndürmek istiyorum:

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

Bu işlevi çağıran kod şöyle görünür:

$returnURL = MyFunction -param 1 ...

Bu yüzden bir String bekliyorum, ancak değil. Bunun yerine, System.Management.Automation.PSMethod türünde bir nesnedir. Neden bir String türü yerine bu türü döndürüyor?


2
İşlev bildirimini görebilir miyiz?
zdan

1
Hayır, işlev çağırma değil, bize "İşlevim" i nasıl / nerede bildirdiğinizi gösterin. Ne yaparsanız yapın: Get-Item işlevi: \ MyFunction
zdan

1
Bunu ele almak için alternatif bir yol
önerdi

Bu fonksiyonlar / yöntemler ile ilgili yığın taşması ile ilgili en önemli PowerShell sorulardan biri olduğunu düşünüyorum. PowerShell komut dosyası öğrenmeyi planlıyorsanız, bu sayfanın tamamını okumalı ve iyice anlamalısınız. Eğer yapmazsan daha sonra kendinden nefret edeceksin.
Birdman

Yanıtlar:


271

PowerShell, en azından daha geleneksel bir programlama perspektifinden bakıldığında gerçekten tuhaf dönüş semantiğine sahiptir. Kafanı sarmak için iki ana fikir var:

  • Tüm çıktılar yakalanır ve döndürülür
  • Dönüş anahtar kelimesi gerçekten mantıklı bir çıkış noktasını gösterir

Bu nedenle, aşağıdaki iki komut dosyası bloğu aynı şeyi etkili bir şekilde yapacaktır:

$a = "Hello, World"
return $a

 

$a = "Hello, World"
$a
return

İkinci örnekteki $ a değişkeni, boru hattında çıktı olarak bırakılır ve belirtildiği gibi, tüm çıktı döndürülür. Aslında, ikinci örnekte, dönüşü tamamen atlayabilirsiniz ve aynı davranışı elde edersiniz (işlev, doğal olarak tamamlandığında ve çıktıkça dönüş ima edilir).

Daha fazla işlev tanımınız olmadan neden bir PSMethod nesnesi elde ettiğinizi söyleyemem. Tahminimce, muhtemelen yakalanmayan ve çıkış hattına yerleştirilen birkaç satırlık bir şey var.

Tek bir satıra birden fazla ifade yerleştirmediğiniz sürece, muhtemelen bu noktalı virgüllere ihtiyacınız olmadığını da belirtmek gerekir.

Dönüş semantiği hakkında TechNet'teki about_Return sayfasında veya help returnkomutu PowerShell'in kendisinden çağırarak daha fazla bilgi edinebilirsiniz .


13
bir işlevden ölçekleyici değeri döndürmek için herhangi bir yolu var mı?
ChiliYago

10
İşleviniz bir karma değeri döndürürse, sonucu bu şekilde ele alır, ancak diğer komutların çıkışı da dahil olmak üzere işlev gövdesinde herhangi bir şeyi "yankı" yaparsanız ve aniden sonuç karıştırılır.
Luke Puplett

5
İşlev sonucunun bir parçası olarak bu metni döndürmeden bir işlev içinden ekrana bir şey çıktılamak istiyorsanız ,write-debug$DebugPreference = "Continue""SilentlyContinue"
BeowulfNode42 5:14

7
Bu yardımcı oldu, ancak bu stackoverflow.com/questions/22663848/… daha da yardımcı oldu. "... | Out-Null" aracılığıyla çağrılan işlevde komutların / işlev çağrılarının istenmeyen sonuçlarını iletin ve sonra istediğiniz şeyi döndürün.
Straff

1
@LukePuplett echobenim için sorun oldu! Bu küçük yan nokta çok çok önemli. Ben her şeyi takip bir hashtable dönüyordu, ama yine de dönüş değeri beklediğim değildi. Kaldırıldı echove bir cazibe gibi çalıştı.
Ayo I

54

PowerShell'in bu kısmı muhtemelen en aptal yönüdür. Bir işlev sırasında üretilen herhangi bir yabancı çıktı sonucu kirletir. Bazen herhangi bir çıktı olmaz ve daha sonra bazı koşullar altında planlanan dönüş değerinize ek olarak planlanmamış başka bir çıktı da olur.

Bu nedenle, atamayı orijinal işlev çağrısından kaldırırım, böylece çıktı ekranda sona erer ve sonra hata ayıklayıcı penceresinde ( PowerShell ISE kullanarak ) açmayı planladığım bir şey çıkıncaya kadar adım adım ilerlerim .

Değişkenleri dış kapsamlarda ayırmak gibi şeyler bile çıktıya neden olur, [boolean]$isEnabledki bunu yapmazsanız rahatsız edici bir şekilde yanlış çıkarır [boolean]$isEnabled = $false.

Bir başka iyi $someCollection.Add("thing")olan, yeni koleksiyon sayısını tükürüyor.


2
Değişkeni açıkça tanımlanmış bir değerle doğru şekilde başlatmak için en iyi uygulamayı yapmak yerine neden bir ad ayırmak istersiniz?
BeowulfNode42

2
Demek istiyorum ki, o kapsamda kullanılan her değişken için her kapsamın başında bir değişken bildirimleri bloğuna sahipsiniz. Tüm değişkenleri C # da dahil olmak üzere herhangi bir dilde kullanmadan önce hala başlatmanız veya zaten başlatmanız gerekir. Ayrıca önemlisi [boolean]$apowershell yapmak aslında $ a değişkenini bildirmez. Bunu, çizgiyle çizgiyi takip ederek onaylayabilir $a.gettype()ve dize ile bildirip başlattığınızda bu çıktıyı karşılaştırabilirsiniz $a = [boolean]$false.
BeowulfNode42

3
Muhtemelen haklısın, sadece yanlışlıkla yazmayı önlemek için daha açık bir tasarımı tercih ederim.
Luke Puplett

4
Bir programcının bakış açısından geliyor, ben bir PS-n00b olmasına rağmen, bunu PS sözdiziminin birçok can sıkıcı yönünün en kötüsünü bulduğumdan emin değilim. Nasıl biri "x> y" yerine "x -gt y" yazmayı tercih etti? Şüphesiz, yerleşik operatörler daha insan dostu bir sözdizimi kullanabilir mi?
The Dag

5
@TheDag çünkü bu bir kabuk, ikinci programlama dili; >ve <dosyalara / dosyalardan G / Ç akışı yeniden yönlendirmesi için zaten kullanılıyor. >=stdout adlı bir dosyaya yazar =.
TessellatingHeckler

38

PowerShell 5 ile artık sınıflar oluşturabiliyoruz. İşlevinizi bir sınıfa dönüştürün ve dönüş yalnızca nesnenin hemen önündeki nesneyi döndürecektir. İşte gerçekten basit bir örnek.

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

Bu bir işlev olsaydı, beklenen çıktı

Hello World
808979

ancak bir sınıf olarak döndürülen tek şey 808979 tamsayısıdır.


6
Not. Ayrıca staticyöntemleri destekler . Sözdizimine önderlik etme[test_class]::return_what()
Sancarn

1
"Bu bir işlev olsaydı beklenen çıktı 'Merhaba Dünya \ n808979" olurdu - Bunun doğru olduğunu düşünmüyorum? Çıktı değeri her zaman yalnızca son ifadenin, sizin durumunuzda return 808979, işlevde olsun ya da olmasın değeridir .
amn

1
@amn Teşekkürler! Örnek, yalnızca işlev içinde çıktı oluşturduysa döndüreceğinden çok haklısınız. Beni sinirlendiren de bu. Bazen işleviniz çıktı oluşturabilir ve bu çıktı artı return deyimine eklediğiniz herhangi bir değer döndürülür. Bildiğiniz gibi sınıflar yalnızca bildirilen veya geçersiz türünü döndürür. İşlevleri kullanmayı tercih ederseniz, kolay bir yöntem işlevi bir değişkene atamaktır ve dönüş değerinden daha fazla döndürülürse var bir dizidir ve dönüş değeri dizideki son öğe olur.
DaSmokeDog

1
Peki, denilen bir komuttan ne bekliyorsunuz Write-Output? Burada atıfta bulunulan "konsol" çıkışı değil, işlev / cmdlet'in çıktısıdır. Eğer konsolda malzeme dökümü istiyorsanız vardır Write-Verbose, Write-Hostve Write-Errordiğerleri arasında işlev görür. Temel olarak, anlambilimine bir konsol çıkış prosedüründen Write-Outputdaha yakındır return. Kötüye kullanma Write-Verbose, ayrıntı için kullanın , bunun için.
amn

1
@amn Bunun konsol olmadığını çok iyi biliyorum. Bir dönüşün, bir işleve karşı bir işlevin içindeki beklenmedik çıktı ile nasıl başa çıktığını göstermenin hızlı bir yoluydu. bir işlevde tüm çıktılar + döndürdüğünüz değerlerin tümü geri aktarılır. ANCAK sadece bir sınıfın dönüşünde belirtilen değer paketi geçirilir. Onları kendiniz için çalıştırmayı deneyin.
DaSmokeDog

31

Çözüm olarak dizideki son nesneyi işleve geri döndürüyorum ... Harika bir çözüm değil, ama hiç yoktan iyidir:

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

$b = someFunction
$b = $b[($b.count - 1)]  # Or
$b = $b[-1]              # Simpler

Sonuçta, aynı şeyi yazmanın daha tek satırlık bir yolu olabilir:

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

7
$b = $b[-1]son öğeyi veya diziyi almanın daha basit bir yolu olabilir, ancak daha basit bile istemediğiniz değerlerin çıktısını almak değildir.
Duncan

1
@Duncan Peki ya fonksiyondaki şeylerin çıktısını almak istersem, aynı zamanda bir dönüş değeri istiyorum.
Utkan Gezer

@ThoAppelsin, terminale bir şeyler yazmak istediğinizi write-hostdüşünüyorsanız, doğrudan çıktı almak için kullanın.
Duncan

7

Konsola çıkış yapmak istediğim için dönüş çılgınlığını önlemek için tek bir sonuç üyesi ile basit bir Hashtable nesnesinin etrafından geçiyorum. Referans yoluyla paso yoluyla hareket eder.

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

Gerçek örnek kullanımını GitHub projem unpackTunes'da görebilirsiniz .


4

Koda bakmadan söylemek zor. İşlevinizin birden fazla nesne döndürmediğinden ve diğer aramalardan alınan sonuçları yakaladığınızdan emin olun. Ne için alırsınız:

@($returnURL).count

Her neyse, iki öneri:

Nesneyi dizeye yayınlayın:

...
return [string]$rs

Ya da sadece yukarıdakiyle aynı, ancak yazmak için daha kısa olan çift tırnak içine alın:

...
return "$rs"

1
Ya da sadece "$rs"- returnsadece işlevden erken döndüğünde gereklidir. Geri dönüşü bırakmak daha iyi PowerShell deyimidir.
David Clarke

4

Luke'un bu senaryolarda fonksiyon sonuçlarıyla ilgili açıklaması doğru görünüyor. Sadece temel nedeni anlamak istiyorum ve PowerShell ürün ekibi davranış hakkında bir şeyler yapar. Oldukça yaygın görünüyor ve hata ayıklama zamanı bana çok pahalıya mal oldu.

Bu soruna geçici bir çözüm bulmak için, işlev çağrısındaki değeri döndürmek ve kullanmak yerine genel değişkenler kullanıyorum.

Genel değişkenlerin kullanımına ilişkin başka bir soru: Genel değişken adının işleve geçirilen bir değişken olduğu bir işlevden genel bir PowerShell değişkeni ayarlama


3

Aşağıdaki cevap olarak 4'ü döndürür. Dizeler için deyim ekleme ifadesini değiştirdiğinizde, ilk dizeyi döndürür.

Function StartingMain {
  $a = 1 + 3
  $b = 2 + 5
  $c = 3 + 7
  Return $a
}

Function StartingEnd($b) {
  Write-Host $b
}

StartingEnd(StartingMain)

Bu, bir dizi için de yapılabilir. Aşağıdaki örnekte "Metin 2" döndürülecektir

Function StartingMain {
  $a = ,@("Text 1","Text 2","Text 3")
  Return $a
}

Function StartingEnd($b) {
  Write-Host $b[1]
}

StartingEnd(StartingMain)

İşlevin altındaki işlevi çağırmanız gerektiğini unutmayın. Aksi takdirde, ilk çalıştırıldığında "StartingMain" in ne olduğunu bilmediği bir hata döndürür.


2

Mevcut cevaplar doğrudur, ancak bazen aslında a Write-Outputveya a ile açıkça bir şey döndürmüyorsunuz return, ancak işlev sonuçlarında bazı gizemli değerler vardır. Bu gibi yerleşik bir fonksiyonun çıktısı olabilirNew-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

Dizin oluşturma işleminin tüm bu ekstra gürültüsü çıktıda toplanıyor ve yayılıyor. Bunu hafifletmenin kolay yolu | Out-Null, New-Itemifadenin sonuna eklemektir veya sonucu bir değişkene atayabilir ve yalnızca bu değişkeni kullanamazsınız. Şöyle görünecektir ...

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-ItemMuhtemelen bunlardan daha meşhurdur, ancak diğerleri tüm StringBuilder.Append*()yöntemleri ve SqlDataAdapter.Fill()yöntemi içerir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.