我开发了一个 PowerShell 函数,它执行许多涉及配置 SharePoint 团队网站的操作。最终,我希望该函数将配置站点的 URL 作为字符串返回,因此在我的函数结束时,我有以下代码:
$rs = $url.ToString();
return $rs;
调用此函数的代码如下所示:
$returnURL = MyFunction -param 1 ...
所以我期待一个字符串,但事实并非如此。相反,它是 System.Management.Automation.PSMethod 类型的对象。为什么它返回该类型而不是 String 类型?
PowerShell 具有非常古怪的返回语义——至少从更传统的编程角度来看是这样。有两个主要想法可以帮助您:
所有输出都被捕获并返回
return 关键字实际上只是表示一个逻辑退出点
因此,以下两个脚本块将有效地完成完全相同的事情:
$a = "Hello, World"
return $a
$a = "Hello, World"
$a
return
第二个示例中的 $a 变量作为输出保留在管道上,并且如前所述,返回所有输出。事实上,在第二个示例中,您可以完全省略返回,并且您将获得相同的行为(返回将隐含在函数自然完成和退出时)。
如果没有更多的函数定义,我不能说为什么你会得到一个 PSMethod 对象。我的猜测是,您可能有几行没有被捕获并被放置在输出管道上的东西。
还值得注意的是,您可能不需要这些分号 - 除非您将多个表达式嵌套在一行中。
您可以在 TechNet 上的 about_Return 页面上阅读有关返回语义的更多信息,或者从 PowerShell 本身调用 help return
命令。
PowerShell 的这一部分可能是最愚蠢的方面。函数期间生成的任何无关输出都会污染结果。有时没有任何输出,然后在某些情况下,除了您的计划返回值之外,还有一些其他计划外输出。
因此,我从原始函数调用中删除了分配,因此输出最终出现在屏幕上,然后逐步执行,直到在调试器窗口中弹出我没有计划的内容(使用 PowerShell ISE)。
甚至像在外部作用域中保留变量这样的事情也会导致输出,比如 [boolean]$isEnabled
除非您将其设为 [boolean]$isEnabled = $false
,否则它会烦人地吐出 False。
另一个好方法是 $someCollection.Add("thing")
,它会吐出新的集合计数。
[boolean]$a
实际上并没有声明变量 $a。您可以通过跟随该行与行 $a.gettype()
来确认这一点,并将该输出与您声明和初始化字符串 $a = [boolean]$false
时的输出进行比较。
>
和 <
已用于 I/O 流重定向到/来自文件。 >=
将标准输出写入名为 =
的文件。
使用 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。
static
方法。导致语法 [test_class]::return_what()
return 808979
,函数与否。
Write-Output
的命令有何期望?这里提到的不是“控制台”输出,而是函数/cmdlet 的输出。如果您想在控制台上转储内容,则有 Write-Verbose
、Write-Host
和 Write-Error
函数等。基本上,Write-Output
更接近 return
语义,而不是控制台输出过程。不要滥用它,使用 Write-Verbose
表示冗长,这就是它的用途。
作为一种解决方法,我一直在返回从函数返回的数组中的最后一个对象......这不是一个很好的解决方案,但总比没有好:
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]
$b = $b[-1]
将是获取最后一个元素或数组的更简单方法,但更简单的是不输出您不想要的值。
write-host
直接输出。
我传递了一个带有单个结果成员的简单 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 project unpackTunes 中查看实际示例用法。
现有答案是正确的,但有时您实际上并没有使用 Write-Output
或 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()
方法。
Out-Default
如果您确实想显示它但不希望它成为返回值。
不看代码很难说。确保您的函数返回的对象不超过一个,并确保您捕获来自其他调用的任何结果。你得到什么:
@($returnURL).count
无论如何,有两个建议:
将对象转换为字符串:
...
return [string]$rs
或者只是用双引号将其括起来,与上面相同,但键入更短:
...
return "$rs"
"$rs"
- return
仅在从函数提前返回时才需要。省略返回是更好的 PowerShell 习惯用法。
Luke's description of the function results 在这些场景中似乎是正确的。我只想了解根本原因,PowerShell 产品团队会对这种行为采取一些措施。这似乎很常见,并且花费了我太多的调试时间。
为了解决这个问题,我一直在使用全局变量,而不是返回并使用函数调用中的值。
这里还有一个关于使用全局变量的问题:Setting a global PowerShell variable from a function where the global variable name is a variable passed to the function
下面简单地返回 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”是什么。
您需要在返回之前清除输出。尝试使用 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
$DebugPreference = "Continue"
而不是"SilentlyContinue"
后使用命令write-debug
echo
对我来说是个问题!这一点非常重要。我正在返回一个哈希表,遵循其他所有内容,但返回值仍然不是我所期望的。删除了echo
并像魅力一样工作。