PowerShell中的函数返回值

Chi*_*ago 168 powershell

我开发了一个PowerShell函数,它执行许多涉及配置SharePoint Team站点的操作.最终,我希望函数将配置站点的URL作为String返回,因此在我的函数结束时,我有以下代码:

$rs = $url.ToString();
return $rs;
Run Code Online (Sandbox Code Playgroud)

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

$returnURL = MyFunction -param 1 ...
Run Code Online (Sandbox Code Playgroud)

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

Goy*_*uix 254

PowerShell具有非常古怪的返回语义 - 至少从更传统的编程角度来看是这样.有两个主要想法可以解决问题:

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

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

$a = "Hello, World"
return $a
Run Code Online (Sandbox Code Playgroud)

 

$a = "Hello, World"
$a
return
Run Code Online (Sandbox Code Playgroud)

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

如果没有更多的函数定义,我无法说明为什么要获得PSMethod对象.我的猜测是你可能有几行没有被捕获并被放置在输出管道上.

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

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

  • 那么有没有办法从函数中返回一个缩放器值? (12认同)
  • 如果你的函数返回一个哈希表,那么它将如此处理结果,但是如果你"回显"函数体内的任何东西,包括其他命令的输出,突然结果是混合的. (9认同)
  • 这有所帮助,但这个http://stackoverflow.com/questions/22663848/powershell-function-doesnt-have-proper-return-value帮助了更多.通过"... | Out-Null"在被调用函数中管理不需要的命令/函数调用结果,然后返回所需的东西. (6认同)
  • 如果你想从函数内部输出东西而不返回该文本作为函数结果的一部分,在设置变量`$ DebugPreference ="Continue"`而不是`"SilentlyContinue"之后使用命令`write-debug` ` (5认同)
  • @LukePuplett `echo` 是我的问题!那一点点非常非常重要。我正在返回一个哈希表,遵循其他所有内容,但返回值仍然不是我所期望的。删除了 `echo` 并像魅力一样工作。 (2认同)

Luk*_*ett 48

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

所以,我所做的是从原始函数调用中删除赋值,以便输出在屏幕上结束,然后逐步执行,直到我没有计划在调试器窗口中弹出的内容(使用PS ISE).

甚至像保留外部作用域中的变量之类的东西[boolean]$isEnabled也会导致输出,除非你这样做,否则会烦恼地吐出一个False [boolean]$isEnabled = $false.

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

  • @TheDag,因为它首先是shell,编程语言是第二; `>`和`<`已用于I/O流重定向到/从文件.`> =`将stdout写入一个名为`=`的文件. (5认同)
  • 从程序员的角度来看,虽然我是PS-n00b,但我并不确定这是PS语法中许多讨厌的方面中最糟糕的.究竟怎么认为写"x -gt y"而不是"x> y"更好???当然至少内置运算符可以使用更人性化的语法? (4认同)
  • 你可能是对的,我只是更喜欢更明确的设计来防止意外写入. (3认同)
  • 为什么要保留一个名称而不是使用明确定义的值正确初始化变量的最佳实践. (2认同)
  • 我认为您的意思是在每个作用域的开头为该作用域中使用的每个变量都有一个变量声明块。在任何语言(包括 C#)中使用所有变量之前,您应该仍然或可能已经初始化所有变量。同样重要的是,在 powershell 中执行 `[boolean]$a` 实际上并没有声明变量 $a。您可以通过使用“$a.gettype()”行来确认这一点,并将该输出与您使用字符串“$a = [boolean]$false”声明和初始化时进行比较。 (2认同)
  • “我倾向于在动态语言中遇到麻烦”这就是为什么即使编译器为您处理事情,用任何语言初始化变量都是一个很好的做法,因为它使您的技能/习惯更加可移植,并且代码稍微当其他人阅读它时,就不那么含糊了。 (2认同)

Joh*_*ker 36

使用PowerShell 5,我们现在可以创建类.将函数更改为类不仅可以键入它,而且只返回它之前的对象.这是一个非常简单的例子.

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

如果这是一个函数,那么预期的输出就是

Hello World
808979
Run Code Online (Sandbox Code Playgroud)

但是作为一个类,唯一返回的是INT 808979.类是一种保证,它只返回声明的类型或void.

  • 注意.它还支持`static`方法.导致语法`[test_class] :: return_what()` (6认同)
  • @amn 谢谢你!您说得非常正确,该示例仅在函数内创建输出时才会返回该值。这让我很恼火。有时,您的函数可能会生成输出,并且该输出加上您在 return 语句中添加的任何值都会被返回。如您所知,类只会返回声明的类型或 void。如果您更喜欢使用函数,那么一种简单的方法是将函数分配给变量,如果返回的值多于返回值,则 var 是一个数组,返回值将是数组中的最后一项。 (2认同)
  • 好吧,您对名为“Write-Output”的命令有何期望?这里提到的不是“控制台”输出,而是函数/cmdlet 的输出。如果您想在控制台上转储内容,则可以使用 `Write-Verbose`、`Write-Host` 和 `Write-Error` 函数等。基本上,`Write-Output` 更接近于 `return` 语义,而不是控制台输出过程。不要滥用它,使用 `Write-Verbose` 来表示冗长,这就是它的用途。 (2认同)
  • @amn 我很清楚这不是控制台。这是一种快速的方式来显示返回处理函数与类中的意外输出的方式。在一个函数中,所有输出+您返回的值都被传回。然而,只有在类的返回中指定的值才会被传递包。尝试自己运行它们。 (2认同)

小智 26

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

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

$b = someFunction
$b = $b[($b.count - 1)]  # Or
$b = $b[-1]              # Simpler
Run Code Online (Sandbox Code Playgroud)

  • `$ b = $ b [-1]`是获取最后一个元素或数组的一种更简单的方法,但更简单的方法就是不输出你不想要的值. (7认同)

3lv*_*naz 12

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

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

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

$result = Return-Url -url "/sf/ask/720031511/"

Write-Host $result
Write-Host $result.GetType()
Run Code Online (Sandbox Code Playgroud)

结果是:

/sf/ask/720031511/
System.String
Run Code Online (Sandbox Code Playgroud)

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


Sti*_*ack 6

现有答案是正确的,但有时您实际上并未使用 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
Run Code Online (Sandbox Code Playgroud)

目录创建的所有额外噪音都被收集并在输出中发出。缓解这种情况的简单方法是添加| 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
Run Code Online (Sandbox Code Playgroud)

New-Item可能是其中最著名的,但其他包括所有StringBuilder.Append*()方法,以及SqlDataAdapter.Fill()方法。

  • 或者如果您确实想显示它但不希望它成为返回值,则选择“Out-Default”。 (2认同)

Sha*_*evy 5

不看代码很难说。确保您的函数不会返回多个对象,并且您捕获了其他调用产生的任何结果。你得到什么:

@($returnURL).count
Run Code Online (Sandbox Code Playgroud)

无论如何,两个建议:

将对象转换为字符串:

...
return [string]$rs
Run Code Online (Sandbox Code Playgroud)

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

...
return "$rs"
Run Code Online (Sandbox Code Playgroud)


小智 5

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

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

这是关于使用全局变量的另一个问题: Setting a global PowerShell variable from a function 其中全局变量名称是传递给函数的变量


Wha*_*ool 5

我使用单个结果成员传递一个简单的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
Run Code Online (Sandbox Code Playgroud)

您可以在我的GitHub项目unpackTunes中看到真实的示例用法