我从这个StackOverflow question中学到了PowerShell
返回语义不同,让我们从C#
的返回语义中说出来。引用上述问题:
PowerShell具有非常古怪的返回语义 - 至少从更传统的编程角度来看。有两个主要想法可以解决:所有输出都被捕获并返回。 return关键字实际上只是表示逻辑退出点。
让我们来看看这个例子:
function Calculate
{
echo "Calculate"
return 11
}
$result = Calculate
如果你回复$result
,你会发现某些事情是不对的。你期待这个:
11
但实际上,你实际看到的是不同的:
Calculate
11
因此,您实际上不会返回预期的返回值,而是返回一个数组。
你可以摆脱echo语句并且不会污染返回值,但是如果你从你的函数中调用另一个函数,它会回复某些东西,那么你就会再次陷入困境。
我的问题是,如何编写PowerShell
函数,只返回一件事。如果内置的PowerShell
函数只返回一个值,为什么我不这样做呢?
我现在正在使用的解决方法,我很乐意摆脱:
function Calculate
{
#Every function that returns has to echo something
echo ""
return 11
}
#The return values is the last value from the returning array
$result = (Calculate)[-1]
答案 0 :(得分:17)
您也可以将所有内容分配为null,然后只返回您想要的变量:
Function TestReturn {
$Null = @(
Write-Output "Hi there!"
1 + 1
$host
$ReturnVar = 5
)
return $ReturnVar
}
答案 1 :(得分:9)
已经给出的几个答案没有完全回答您的问题,因为他们没有考虑您可能会调用的其他 cmdlet - 正如您所指出的那样 - 可能会“污染”您的输出。来自@Laezylion的答案基本上是正确的,但我相信还有进一步的阐述。
很明显,您了解当前的解决方法 - 强制每个函数返回一个数组然后采用最后一个元素 - 并不是一个非常强大的解决方案。 (事实上,我认为将其称为kludge是安全的。:-)正确的方法 - 以及对问题的直接回答 - 很清楚......虽然乍一看它听起来像是重言式:
Question: How do you ensure a function returns only one thing?
Answer: By ensuring that it returns only one thing.
让我澄清一下。 PowerShell中实际上有两种函数/ cmdlet:(A)返回数据的函数和(B)那些不返回数据但可以报告进度或诊断消息的函数/ cmdlet。正如您所看到的,当您尝试将两者混合时出现问题。这可能很容易在无意中发生。因此,作为函数作者,您有责任理解函数中的每个函数/ cmdlet调用:具体而言,它是否返回任何内容,无论是真正的返回值还是仅仅是诊断输出?您期望从A类函数输出,但您需要警惕任何类型B函数。对于任何确实返回某个内容的人,您必须将其分配给变量,将其提供给管道,或者......让它消失。 (有几种方法可以消除它:将它传递给Out-Null,将其转换为void,将其重定向到$ null,或者将其分配给$ null。请参阅Jason Archer的Stack Overflow post来评估每个的性能这些口味,暗示你回避Out-Null。)
是的,这种方法需要付出更多努力,但通过这样做可以获得更强大的解决方案。有关这个问题的更多讨论,这些A / B功能等,请参阅最近在Simple-Talk.com上发布的A Plethora of PowerShell Pitfalls上的问题1。
答案 2 :(得分:6)
请勿使用echo
来撰写信息,使用Write-Host
,Write-Verbose
或Write-Debug
,具体取决于邮件。
Echo
是Write-Output
的别名,它将通过管道将其作为Object发送。 Write-Host/Verbose/Debug
但只是控制台消息。
PS P:\> alias echo
CommandType Name ModuleName
----------- ---- ----------
Alias echo -> Write-Output
样品:
function test {
Write-Host calc
return 11
}
$t = test
"Objects returned: $($t.count)"
$t
#Output
calc
Objects returned: 1
11
另外,如果在函数的最后一行返回值,则不需要使用return
。单独编写Object / value就足够了。
function test {
Write-Host calc
11
}
不同之处在于return 11
在函数中间使用后会跳过这些行。
function test {
return 11
12
}
test
#Output
11
答案 3 :(得分:6)
值得一提: 函数内的任何非托管命令输出都将以返回值结束。
我用管理XML内容的函数来解决这个问题:
Function Add-Node(NodeName){
$NewXmlNode = $Xml.createElement("Item")
$Xml.Items.appendChild($NewXmlNode) | out-null
}
如果删除Out-Null,函数的返回将是Node基本属性...(您还可以管理appendChild命令的退出代码......取决于动机):)
答案 4 :(得分:2)
您可以将write(写入输出的别名)替换为write-host:
PS>function test{ write-host "foo";"bar"}
PS>$a=test
test
PS>$a
bar
答案 5 :(得分:2)
echo是Write-Output的别名,它将对象发送到下一个命令。
使用Write-Host应该有帮助。
PS:> function Calculate
>> {
>> #Every function that returns has to echo something
>> write-host "test"
>> return 11
>> }
>>
PS:> $A=Calculate
test
PS:> $A
11
答案 6 :(得分:2)
@Michael Sorens的答案很有用,但它表明了一种似乎相当繁重的设计策略。我不想将数据处理功能与报告进度或诊断消息的功能分开。 Reading his blogpost,对于OP的问题,我发现了一个不太棘手的解决方法。这是博客文章中的关键句子:
PowerShell函数返回所有未捕获的输出。
那如果我们捕获输出结果呢?
function Calculate
{
$junkOutput = echo "Calculate"
return 11
}
$result = Calculate
现在$result
仅包含11个。
答案 7 :(得分:0)
在调用接受参数的函数时,使用括号显得至关重要。 本示例从SQL Server填充DataTable,并从第一行第四列返回[int]。 注意分配给变量[int] $ tt1
的实际函数调用的括号 function InvokeSQL-GetOneCount { param( [string]$cn, [string]$prj )
$sql = "SELECT * FROM [dbo].[dummytable] WHERE ProjectNumber = '$prj' ORDER BY FormID, PartID;"
$handler = [System.Data.SqlClient.SqlInfoMessageEventHandler] { param($sender, $event) Handle-Message -message $event.Message -fcolor "yellow" -bcolor "black"; };
$conn = new-Object System.Data.SqlClient.SqlConnection($cn)
$conn.add_InfoMessage($handler);
$conn.FireInfoMessageEventOnUserErrors = $true;
$cmd = New-Object System.Data.SqlClient.SqlCommand;
$cmd.Connection = $conn;
$cmd.CommandType = [System.Data.CommandType]::Text;
$cmd.CommandText = $sql;
$cmd.CommandTimeout = 0;
$sa = New-Object System.Data.SqlClient.SqlDataAdapter($cmd);
$dt = New-Object System.Data.DataTable("AllCounts");
$sa.Fill($dt) | Out-Null;
[int]$rval = $dt.Rows[0][3];
$conn.Close();
return $rval;
}
clear-host;
[int]$tt1 = (InvokeSQL-GetOneCount -cn "Server=[your server];DataBase=[your catalog];Integrated Security=SSPI" -prj "MS1904");
write-host $tt1;
答案 8 :(得分:0)
我最终使用以下命令来确保仅返回一个值:
答案 9 :(得分:0)
PowerShell笨拙地进行管道传输。若要解决返回值问题并保存真实数据的流水线,请通过引用将该函数传递一个值,即数组。该数组可以加载您希望返回的值。这是一个简单的示例:
# tp.ps1
# test passed parameters
# show that arrays are passed by reference, and can be used to provide
# return values outside the pipeline
function doprint
{
process { write-output "value now: $psitem" }
}
function recurse($thing, $rtx)
{
$thing++
if($thing -lt 20) {
if($thing -eq 15) { $rtx[0] = $thing }
write-output $thing | doprint
recurse $thing $rtx
}
}
j=0
$rtv=@(4)
recurse $j $rtv
write-output $rtv[0]