ฟังก์ชันส่งคืนค่าใน PowerShell


188

ฉันได้พัฒนาฟังก์ชัน PowerShell ที่ดำเนินการหลายอย่างที่เกี่ยวข้องกับการจัดเตรียมไซต์SharePoint Team ในที่สุดฉันต้องการฟังก์ชั่นเพื่อกลับ URL ของเว็บไซต์ที่จัดเตรียมเป็นสตริงดังนั้นในตอนท้ายของฟังก์ชั่นของฉันฉันมีรหัสต่อไปนี้:

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

รหัสที่เรียกใช้ฟังก์ชันนี้มีลักษณะดังนี้:

$returnURL = MyFunction -param 1 ...

ดังนั้นฉันคาดหวังสตริง แต่มันไม่ได้ แต่เป็นวัตถุประเภท System.Management.Automation.PSMethod แทน เหตุใดจึงส่งคืนชนิดนั้นแทนที่จะเป็นชนิดสตริง


2
เราเห็นการประกาศฟังก์ชั่นได้ไหม?
zdan

1
ไม่ไม่ใช่การเรียกใช้ฟังก์ชันแสดงให้เราทราบว่าคุณประกาศว่า "MyFunction" ได้อย่างไร จะเกิดอะไรขึ้นเมื่อคุณ: ฟังก์ชั่นรับรายการ: \ MyFunction
zdan

1
แนะนำวิธีอื่นในการแก้ไขปัญหานี้: stackoverflow.com/a/42842865/992301
Feru

ฉันคิดว่านี่เป็นหนึ่งในคำถามที่สำคัญที่สุดของ PowerShell ในสแต็คล้นที่เกี่ยวข้องกับฟังก์ชั่น / วิธีการ หากคุณวางแผนที่จะเรียนรู้สคริปต์ PowerShell คุณควรอ่านทั้งหน้านี้และทำความเข้าใจอย่างละเอียด คุณจะเกลียดตัวเองในภายหลังถ้าไม่ทำ
Birdman

คำตอบ:


271

PowerShell มีความหมายของการส่งคืนที่แปลกประหลาดอย่างน้อยเมื่อดูจากมุมมองการเขียนโปรแกรมแบบดั้งเดิมมากขึ้น มีสองแนวคิดหลักในการพันหัวของคุณ:

  • เอาต์พุตทั้งหมดถูกดักจับและส่งคืน
  • คำหลักที่ส่งคืนจริง ๆ เพียงระบุจุดทางออกตรรกะ

ดังนั้นบล็อกสคริปต์สองบล็อกต่อไปนี้จะทำสิ่งเดียวกันอย่างมีประสิทธิภาพ:

$a = "Hello, World"
return $a

 

$a = "Hello, World"
$a
return

ตัวแปร $ a ในตัวอย่างที่สองถูกปล่อยให้เป็นเอาต์พุตบนไพพ์ไลน์และดังที่กล่าวไว้เอาต์พุตทั้งหมดจะถูกส่งคืน ในความเป็นจริงในตัวอย่างที่สองคุณสามารถละเว้นการส่งคืนได้อย่างสมบูรณ์และคุณจะได้รับพฤติกรรมเดียวกัน

หากไม่มีนิยามฟังก์ชั่นของคุณมากขึ้นฉันไม่สามารถบอกได้ว่าทำไมคุณถึงได้รับวัตถุ PSMethod ฉันเดาว่าคุณอาจมีบางสิ่งบางอย่างที่ไม่ได้ถูกจับและวางลงบนท่อส่งออก

นอกจากนี้ยังเป็นที่น่าสังเกตว่าคุณอาจไม่ต้องการเครื่องหมายอัฒภาคเหล่านั้น - ยกเว้นว่าคุณกำลังซ้อนหลายนิพจน์ในบรรทัดเดียว

คุณสามารถอ่านเพิ่มเติมเกี่ยวกับ semantics ที่ส่งคืนได้ในหน้าabout_Returnบน TechNet หรือโดยเรียกใช้help returnคำสั่งจาก PowerShell


13
ดังนั้นมีวิธีการคืนค่า scaler จากฟังก์ชั่นใด ๆ ?
ChiliYago

10
หากฟังก์ชั่นของคุณส่งคืน hashtable แล้วมันจะปฏิบัติต่อผลลัพธ์เช่นนั้น แต่ถ้าคุณ "สะท้อน" สิ่งใด ๆ ภายในร่างกายของฟังก์ชั่นรวมถึงการส่งออกของคำสั่งอื่น ๆ และทันใดนั้นผลการผสม
Luke Puplett

5
หากคุณต้องการส่งออกข้อมูลไปยังหน้าจอจากภายในฟังก์ชั่นโดยไม่ส่งคืนข้อความนั้นเป็นส่วนหนึ่งของผลการทำงานให้ใช้คำสั่งwrite-debugหลังจากตั้งค่าตัวแปร$DebugPreference = "Continue"แทน"SilentlyContinue"
BeowulfNode42

7
สิ่งนี้ช่วยได้ แต่stackoverflow.com/questions/22663848/…ช่วยได้มากขึ้น ไพพ์ผลลัพธ์ที่ไม่ต้องการของการเรียกใช้คำสั่ง / ฟังก์ชั่นในฟังก์ชั่นที่เรียกผ่าน "... | Out-Null" จากนั้นเพียงแค่คืนสิ่งที่คุณต้องการ
Straff

1
@LukePuplett echoเป็นปัญหาสำหรับฉัน! จุดเล็ก ๆ น้อย ๆ นั้นสำคัญมาก ฉันกลับ hashtable ตามทุกอย่างอื่น แต่ก็ยังมีค่าตอบแทนไม่ใช่สิ่งที่ฉันคาดหวัง นำออกechoและทำงานเหมือนเครื่องราง
Ayo ฉัน

54

ส่วนหนึ่งของ PowerShell นี้อาจเป็นลักษณะที่โง่ที่สุด เอาต์พุตภายนอกใด ๆ ที่สร้างขึ้นระหว่างฟังก์ชั่นจะทำให้เกิดผลที่ไม่พึงประสงค์ บางครั้งไม่มีเอาต์พุตใด ๆ และภายใต้เงื่อนไขบางอย่างก็มีเอาต์พุตอื่นที่ไม่ได้วางแผนไว้นอกเหนือจากค่าส่งคืนที่คุณวางแผนไว้

ดังนั้นฉันจึงลบการมอบหมายจากการเรียกใช้ฟังก์ชั่นดั้งเดิมดังนั้นผลลัพธ์จะจบลงบนหน้าจอจากนั้นก้าวผ่านสิ่งที่ฉันไม่ได้วางแผนที่จะปรากฏขึ้นในหน้าต่างดีบักเกอร์ (ใช้PowerShell ISE )

แม้แต่สิ่งต่าง ๆ เช่นการจองตัวแปรในขอบเขตด้านนอกทำให้เกิดผลลัพธ์เช่น[boolean]$isEnabledที่จะแยกออกเป็นเท็จอย่างน่ารำคาญเว้นแต่คุณจะสร้างมัน[boolean]$isEnabled = $falseขึ้นมา

อีกสิ่งหนึ่งที่ดีคือ$someCollection.Add("thing")การแยกคอลเลกชันใหม่ออกมา


2
ทำไมคุณเพียงแค่จองชื่อแทนที่จะทำแนวปฏิบัติที่ดีที่สุดในการเริ่มต้นตัวแปรอย่างถูกต้องด้วยค่าที่กำหนดไว้อย่างชัดเจน
BeowulfNode42

2
ฉันหมายความว่าคุณมีบล็อกของการประกาศตัวแปรที่จุดเริ่มต้นของแต่ละขอบเขตสำหรับตัวแปรทุกตัวที่ใช้ในขอบเขตนั้น คุณควรจะยังคงหรืออาจจะเริ่มต้นตัวแปรทั้งหมดก่อนที่จะใช้พวกเขาในภาษาใด ๆ รวมทั้ง C # สิ่งสำคัญ[boolean]$aในการทำpowershell ไม่ได้ประกาศตัวแปร $ a คุณสามารถยืนยันนี้โดยทำตามบรรทัดที่เส้นและเปรียบเทียบผลลัพธ์ที่เมื่อคุณประกาศและเริ่มต้นกับสตริง$a.gettype() $a = [boolean]$false
BeowulfNode42

3
คุณอาจจะถูกต้องฉันต้องการการออกแบบที่ชัดเจนมากขึ้นเพื่อป้องกันการเขียนโดยไม่ตั้งใจ
Luke Puplett

4
มาจากมุมมองของโปรแกรมเมอร์ถึงแม้ว่าฉันเป็น PS-n00b แต่ฉันไม่แน่ใจว่าฉันพบว่าสิ่งนี้เลวร้ายที่สุดในแง่มุมที่น่ารำคาญของไวยากรณ์ PS มีใครในโลกคิดว่าควรเขียน "x -gt y" มากกว่า "x> y"?!? อย่างน้อยที่สุดตัวดำเนินการในตัวอาจใช้ไวยากรณ์ที่เป็นมิตรต่อมนุษย์มากกว่า
Dag

5
@TheDag เพราะมันคือเชลล์ตัวแรก, ภาษาโปรแกรมที่สอง; >และ<ถูกใช้สำหรับการเปลี่ยนเส้นทาง I / O สตรีมไปยัง / จากไฟล์ >=เขียน stdout =ไปยังไฟล์ที่เรียกว่า
TessellatingHeckler

38

ด้วย PowerShell 5 ตอนนี้เรามีความสามารถในการสร้างคลาส เปลี่ยนฟังก์ชั่นของคุณลงในชั้นเรียนและผลตอบแทนจะเพียงกลับวัตถุทันทีก่อนหน้านั้น นี่คือตัวอย่างง่ายๆที่แท้จริง

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

หากนี่เป็นฟังก์ชันเอาต์พุตที่คาดหวังจะเป็น

Hello World
808979

แต่เป็นคลาสสิ่งเดียวที่ส่งคืนคือจำนวนเต็ม 808979 คลาสจะเหมือนกับการรับประกันว่ามันจะคืนค่าประเภทที่ประกาศหรือเป็นโมฆะเท่านั้น


6
บันทึก. นอกจากนี้ยังรองรับstaticวิธีการ การนำไปสู่ไวยากรณ์[test_class]::return_what()
Sancarn

1
"ถ้านี่เป็นฟังก์ชั่นผลลัพธ์ที่คาดหวังจะเป็น 'Hello World \ n808979" - ฉันไม่คิดว่ามันถูกต้องเหรอ? ค่าเอาต์พุตเป็นเพียงค่าของข้อความสั่งสุดท้ายเสมอในกรณีของคุณreturn 808979ฟังก์ชั่นหรือไม่
พฤศจิกายน

1
@amn ขอบคุณ! คุณถูกต้องมากตัวอย่างจะส่งคืนเท่านั้นหากสร้างผลลัพธ์ภายในฟังก์ชัน สิ่งใดที่ทำให้ฉันรำคาญ บางครั้งฟังก์ชันของคุณอาจสร้างเอาต์พุตและเอาต์พุตนั้นรวมกับค่าใด ๆ ที่คุณเพิ่มในคำสั่ง return จะได้รับคืน คลาสที่คุณทราบจะส่งคืนชนิดที่ประกาศหรือโมฆะเท่านั้น หากคุณต้องการใช้ฟังก์ชั่นวิธีง่าย ๆ วิธีหนึ่งก็คือการกำหนดฟังก์ชั่นให้กับตัวแปรและหากมีการส่งคืนค่ามากกว่าจะคืนค่า var คืออาร์เรย์และค่าที่ส่งคืนจะเป็นรายการสุดท้ายในอาร์เรย์
DaSmokeDog

1
ดีสิ่งที่คุณคาดหวังจากคำสั่งที่เรียกว่าWrite-Output? ไม่ใช่เอาต์พุต "คอนโซล" ที่อ้างถึงที่นี่ แต่เป็นเอาต์พุตสำหรับฟังก์ชัน / cmdlet ถ้าคุณต้องการที่จะถ่ายโอนข้อมูลสิ่งที่อยู่บนคอนโซลมีWrite-Verbose, Write-HostและWrite-Errorฟังก์ชั่นอื่น ๆ ในกลุ่ม โดยทั่วไปWrite-Outputอยู่ใกล้กับreturnซีแมนทิกส์มากกว่าขั้นตอนเอาต์พุตของคอนโซล อย่าใช้มันในทางที่ผิดใช้Write-Verboseเพื่อความฟุ้งซ่าน
พฤศจิกายน

1
@ ฉันรู้ว่านี่ไม่ใช่คอนโซล มันเป็นวิธีที่รวดเร็วในการแสดงวิธีที่ข้อตกลงการส่งคืนกับเอาต์พุตที่ไม่คาดคิดในฟังก์ชันเทียบกับคลาส ในฟังก์ชั่นเอาต์พุตทั้งหมด + ค่าที่คุณกลับมาจะถูกส่งคืนทั้งหมด HOWEVER เฉพาะค่าที่ระบุในการส่งคืนคลาสจะถูกส่งผ่านแพ็ค ลองใช้ด้วยตัวคุณเอง
DaSmokeDog

31

วิธีแก้ปัญหาฉันได้ส่งคืนวัตถุสุดท้ายในอาร์เรย์ที่คุณได้รับกลับมาจากฟังก์ชัน ... มันไม่ใช่วิธีการแก้ปัญหาที่ยอดเยี่ยม แต่มันดีกว่าไม่มีอะไรเลย:

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

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

โดยรวมแล้ววิธีการเขียนที่เหมือนกันมากกว่าหนึ่งบรรทัดอาจเป็น:

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

7
$b = $b[-1]จะเป็นวิธีที่ง่ายกว่าในการรับองค์ประกอบสุดท้ายหรืออาร์เรย์ แต่ที่เรียบง่ายกว่าก็คือการไม่เอาท์พุทค่าที่คุณไม่ต้องการ
Duncan

@ ดันแคนและถ้าฉันต้องการส่งออกข้อมูลในฟังก์ชั่นในขณะที่ในเวลาเดียวกันฉันก็ต้องการค่าตอบแทน?
Utkan Gezer

@ThoAppelsin ถ้าคุณหมายถึงว่าคุณต้องการเขียนข้อมูลลงในเทอร์มินัลคุณก็สามารถใช้write-hostมันเพื่อส่งออกโดยตรง
Duncan

7

ฉันผ่านวัตถุ Hashtable อย่างง่าย ๆ พร้อมกับสมาชิกผลลัพธ์เดี่ยวเพื่อหลีกเลี่ยงความบ้าคลั่งกลับเนื่องจากฉันยังต้องการส่งออกไปยังคอนโซล มันทำหน้าที่ผ่านการอ้างอิง

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

คุณสามารถดูตัวอย่างการใช้งานจริงของฉันGitHub โครงการ unpackTunes


4

มันยากที่จะพูดโดยไม่ได้ดูที่รหัส ตรวจสอบให้แน่ใจว่าฟังก์ชั่นของคุณไม่ส่งคืนวัตถุมากกว่าหนึ่งวัตถุและให้คุณได้รับผลลัพธ์ที่เกิดจากการโทรอื่น ๆ คุณได้อะไรจาก:

@($returnURL).count

อย่างไรก็ตามคำแนะนำสองข้อ:

ส่งวัตถุไปยังสตริง:

...
return [string]$rs

หรือใส่ไว้ในเครื่องหมายคำพูดคู่เหมือนกับข้างบน แต่สั้นกว่าเพื่อพิมพ์:

...
return "$rs"

1
หรือแม้กระทั่งเพียงแค่"$rs"- returnมันเป็นสิ่งจำเป็นเท่านั้นเมื่อกลับมาก่อนจากฟังก์ชั่น การปล่อยให้ผลตอบแทนดีกว่า PowerShell สำนวน
David Clarke

4

คำอธิบายฟังก์ชั่นของลุคทำให้ผลลัพธ์ในสถานการณ์เหล่านี้ดูเหมือนจะถูกต้อง ฉันเพียงต้องการเข้าใจสาเหตุของปัญหาและทีมผลิตภัณฑ์ PowerShell จะทำอะไรบางอย่างเกี่ยวกับพฤติกรรม ดูเหมือนจะค่อนข้างบ่อยและใช้เวลาในการดีบั๊กมากเกินไป

เพื่อแก้ไขปัญหานี้ฉันใช้ตัวแปรทั่วโลกแทนที่จะส่งคืนและใช้ค่าจากการเรียกใช้ฟังก์ชัน

นี่คือคำถามอีกข้อเกี่ยวกับการใช้ตัวแปรโกลบอล: การตั้งค่าตัวแปร PowerShell ทั่วโลกจากฟังก์ชันที่ชื่อตัวแปรส่วนกลางเป็นตัวแปรที่ส่งผ่านไปยังฟังก์ชัน


3

ต่อไปนี้จะคืนค่า 4 เป็นคำตอบ เมื่อคุณแทนที่นิพจน์การเพิ่มสำหรับสตริงมันจะส่งคืนสตริงแรก

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

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

StartingEnd(StartingMain)

สิ่งนี้สามารถทำได้สำหรับอาร์เรย์ ตัวอย่างด้านล่างจะส่งคืน "ข้อความ 2"

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

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

StartingEnd(StartingMain)

โปรดทราบว่าคุณต้องเรียกใช้ฟังก์ชันด้านล่างของฟังก์ชันนั้นเอง มิฉะนั้นครั้งแรกที่รันมันจะส่งคืนข้อผิดพลาดที่ไม่ทราบว่า "StartingMain" คืออะไร


2

คำตอบที่มีอยู่นั้นถูกต้อง แต่บางครั้งคุณไม่ได้คืนสิ่งที่ชัดเจนด้วย a Write-Outputหรือ a returnแต่มีค่าความลึกลับบางอย่างในผลลัพธ์ของฟังก์ชัน นี่อาจเป็นผลลัพธ์ของฟังก์ชันบิวอินเช่น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

เสียงรบกวนทั้งหมดของการสร้างไดเรกทอรีจะถูกรวบรวมและส่งออกไปยังเอาต์พุต วิธีง่ายๆในการลดสิ่งนี้คือการเพิ่ม| Out-Nullไปยังส่วนท้ายของNew-Itemคำสั่งหรือคุณสามารถกำหนดผลลัพธ์ให้กับตัวแปรและไม่ใช้ตัวแปรนั้น มันจะมีลักษณะเช่นนี้ ...

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อาจมีชื่อเสียงมากขึ้นในเหล่านี้ แต่คนอื่น ๆ รวมถึงStringBuilder.Append*()วิธีการทั้งหมดเช่นเดียวกับSqlDataAdapter.Fill()วิธีการ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.