PowerShell中的函数返回值


188

我已经开发了一个PowerShell函数,该函数执行许多有关配置SharePoint Team网站的操作。最终,我希望函数以String的形式返回预配置站点的URL,因此在函数末尾,我具有以下代码:

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

调用此函数的代码如下:

$returnURL = MyFunction -param 1 ...

所以我期待一个String,但是不是。而是,它是System.Management.Automation.PSMethod类型的对象。为什么返回该类型而不是String类型?


2
我们可以看到函数声明吗?
zdan 2012年

1
不,不是函数调用,而是告诉我们声明“ MyFunction”的方式/位置。当您执行操作时会发生什么:Get-Item函数:\ MyFunction
zdan 2012年

1
建议解决此问题的另一种方法: stackoverflow.com/a/42842865/992301
Feru

我认为这是与函数/方法有关的堆栈溢出最重要的PowerShell问题之一。如果您打算学习PowerShell脚本,则应阅读整个页面并全面了解它。如果不这样做,以后会讨厌自己。
Birdman

Answers:


271

PowerShell具有真正古怪的返回语义-至少从更传统的编程角度来看时是如此。有两个主要想法可以帮助您解决问题:

  • 捕获所有输出,并返回
  • return关键字实际上只是指示逻辑出口点

因此,以下两个脚本块将有效地执行完全相同的操作:

$a = "Hello, World"
return $a

 

$a = "Hello, World"
$a
return

第二个示例中的$ a变量在管道上保留为输出,并且如上所述,返回了所有输出。实际上,在第二个示例中,您可以完全省略该返回值,并且您将获得相同的行为(当函数自然完成并退出时,将暗示该返回值)。

没有更多的函数定义,我无法说出为什么要得到PSMethod对象。我的猜测是您可能有一些未捕获的行,而是被放置在输出管道上的行。

还值得注意的是,您可能不需要这些分号-除非您将多个表达式嵌套在一行上。

您可以在TechNet 的about_Return页面上或通过help return从PowerShell本身调用命令来了解有关返回语义的更多信息。


13
那么有什么方法可以从函数返回定标器值?
ChiliYago 2012年

10
如果您的函数返回哈希表,则它将按原样对待结果,但是如果您在函数主体中“回显”任何东西(包括其他命令的输出),结果将突然混合。
路加·普普利特

5
如果要从函数内部向屏幕输出内容而不返回文本作为函数结果的一部分,请write-debug在设置变量后使用命令,$DebugPreference = "Continue"而不是"SilentlyContinue"
BeowulfNode42

6
这有所帮助,但是stackoverflow.com/questions/22663848/…甚至提供了更多帮助。通过“ ... | Out-Null”,在调用的函数中管道传递命令/函数调用的不需要的结果,然后仅返回您想要的东西。
斯特拉夫,2015年

1
@LukePuplett echo是我的问题!这个小问题非常重要。我正在返回一个哈希表,跟随其他所有操作,但是返回值仍然不是我期望的。删除了echo,并像魅力一样工作。
Ayo I

53

PowerShell的这一部分可能是最愚蠢的方面。函数执行期间生成的任何无关输出都会污染结果。有时没有任何输出,然后在某些情况下,除了计划的返回值之外,还有一些其他计划外的输出。

因此,我从原始函数调用中删除了赋值,因此输出最终显示在屏幕上,然后逐步执行,直到我不打算进行的操作在调试器窗口中弹出(使用PowerShell ISE)。

甚至像在外部范围中保留变量之类的东西[boolean]$isEnabled也会导致输出,例如,除非您这样做,否则它会烦人地吐出False [boolean]$isEnabled = $false

另一个好方法是$someCollection.Add("thing")吐出新的收藏计数。


2
为什么您只保留一个名称,而不是执行使用显式定义的值正确初始化变量的最佳实践呢?
BeowulfNode42 2014年

2
我认为您的意思是,对于每个作用域中使用的每个变量,在每个作用域的开头都有一个变量声明块。在以任何语言(包括C#)使用变量之前,您应该仍然或者可能已经在初始化所有变量。同样重要的是,[boolean]$a在powershell中执行操作实际上并不声明变量$ a。您可以通过在该行之后跟随该行来进行确认$a.gettype(),并将输出与声明和初始化字符串时进行比较$a = [boolean]$false
BeowulfNode42

3
您可能是对的,我只希望使用更明确的设计来防止意外写入。
路加·普普利特

4
从程序员的角度来看,尽管我是PS-n00b,但我不确定在PS语法的许多令人讨厌的方面中,这是否是最糟糕的。在地球上,有人认为写“ x -gt y”而不是“ x> y”更可取吗?至少内置运算符肯定可以使用更人性化的语法吗?
达格2015年

5
@TheDag,因为它首先是外壳程序,其次是编程语言;>并且<已经用于I / O流重定向到文件或从文件重定向。>=将stdout写入名为的文件=
TessellatingHeckler,2015年

38

使用PowerShell 5,我们现在可以创建类。将函数更改为类,然后return将返回紧接其之前的对象。这是一个简单的例子。

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。一个类有点像保证它只会返回声明的类型或void的保证。


6
注意。它还支持static方法。导致语法[test_class]::return_what()
Sancarn

1
“如果这是一个函数,则预期输出将是'Hello World \ n808979”-我认为这是不正确的?输出值始终仅是最后一条语句的值return 808979,无论您是否为函数,都不是。
AMN

1
@amn谢谢!您是非常正确的,该示例仅在函数内创建输出时才返回该示例。这让我很烦。有时,您的函数可能会生成输出,并且该输出以及您在return语句中添加的所有值都会被返回。您知道的类将仅返回声明的类型或void。如果您更喜欢使用函数,则一种简单的方法是将该函数分配给变量,并且如果返回的返回值超过返回值,则var是一个数组,而返回值将是数组中的最后一项。
DaSmokeDog

1
好吧,您对名为的命令有什么期望Write-Output?这里不是指“控制台”输出,而是function / cmdlet的输出。如果你想甩在控制台上的东西也有Write-VerboseWrite-HostWrite-Error功能等等。基本上,Write-Outputreturn比控制台输出过程更接近语义。不要滥用它,Write-Verbose用于冗长,这就是它的目的。
AMN

1
@amn我很清楚这不是控制台。这是一种简单的方法,可以快速显示返回与函数或类内部意外输出的关系。在一个函数中,所有输出+您返回的值都被传回。但是,只有在类的返回中指定的值才能传递给pack。尝试自己运行它们。
DaSmokeDog

30

作为一种解决方法,我一直在返回从函数中返回的数组中的最后一个对象……这不是一个很好的解决方案,但是总比没有好:

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它直接输出。
邓肯

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习惯用法。
大卫·克拉克


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-NullNew-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()方法。


0

作为一种替代,return我建议Write-Output

Write-Output 可以从管道中获取输入,然后将其转发到管道中,或者将其打印到控制台(如果这是最后一条命令)。

但是,Write-Output不会像结束脚本那样结束脚本return,如果应该在循环中使用,转发输出等,这可能是一个优势。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.