225

我开发了一个 PowerShell 函数,它执行许多涉及预配SharePoint团队网站的操作。最终,我希望该函数将配置站点的 URL 作为字符串返回,因此在我的函数结束时,我有以下代码:

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

调用此函数的代码如下所示:

$returnURL = MyFunction -param 1 ...

所以我期待一个字符串,但事实并非如此。相反,它是 System.Management.Automation.PSMethod 类型的对象。为什么它返回该类型而不是 String 类型?

4

10 回答 10

307

PowerShell 具有非常古怪的返回语义——至少从更传统的编程角度来看是这样。有两个主要想法可以帮助您:

  • 所有输出都被捕获并返回
  • return 关键字实际上只是表示一个逻辑退出点

因此,以下两个脚本块将有效地完成完全相同的事情:

$a = "Hello, World"
return $a

 

$a = "Hello, World"
$a
return

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

如果没有更多的函数定义,我不能说为什么你会得到一个 PSMethod 对象。我的猜测是,您可能有几行没有被捕获并被放置在输出管道上的东西。

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

您可以在 TechNet 上的about_Return页面上阅读有关返回语义的更多信息,或者help return从 PowerShell 本身调用命令。

于 2012-04-23T21:01:56.683 回答
59

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

因此,我从原始函数调用中删除了分配,因此输出最终出现在屏幕上,然后逐步执行,直到调试器窗口中弹出我没有计划的内容(使用PowerShell ISE)。

即使是在外部范围内保留变量之类的事情也会导致输出,比如[boolean]$isEnabled除非你成功,否则它会烦人地吐出一个 False [boolean]$isEnabled = $false

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

于 2013-01-30T14:23:57.200 回答
54

使用 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。

于 2017-03-12T02:46:34.563 回答
36

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

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]
于 2014-07-08T11:38:03.627 回答
8

我传递了一个带有单个结果成员的简单 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中查看真实的示例用法。

于 2015-08-23T23:27:31.957 回答
7

现有答案是正确的,但有时您实际上并没有使用 aWrite-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()方法。

于 2020-02-09T21:41:37.777 回答
5

不看代码很难说。确保您的函数返回的对象不超过一个,并确保您捕获来自其他调用的任何结果。你得到什么:

@($returnURL).count

无论如何,有两个建议:

将对象转换为字符串:

...
return [string]$rs

或者只是用双引号将其括起来,与上面相同,但键入更短:

...
return "$rs"
于 2012-04-23T21:02:33.723 回答
4

卢克对这些场景中的函数结果的描述似乎是正确的。我只想了解根本原因,PowerShell 产品团队会对这种行为采取一些措施。这似乎很常见,并且花费了我太多的调试时间。

为了解决这个问题,我一直在使用全局变量,而不是返回并使用函数调用中的值。

这是关于使用全局变量的另一个问题: Setting a global PowerShell variable from a function where the global variable name is a variable passing to the function

于 2013-08-16T19:37:39.943 回答
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”是什么。

于 2014-07-17T06:49:34.193 回答
1

您需要在返回之前清除输出。尝试使用Out-Null。这就是powershell的return工作原理。它返回的不是您想要的变量,而是整个函数的输出。所以你的例子是:

function Return-Url
{
   param([string] $url)

   . {
       $rs = $url.ToString();
       return
   } | Out-Null
   
   return $rs
}

$result = Return-Url -url "https://stackoverflow.com/questions/10286164/function-return-value-in-powershell"

Write-Host $result
Write-Host $result.GetType()

结果是:

https://stackoverflow.com/questions/10286164/function-return-value-in-powershell
System.String

归功于https://riptutorial.com/powershell/example/27037/how-to-work-with-functions-returns

于 2021-08-26T16:58:21.933 回答