Powershell 中的显式 Return

Explicit Return in Powershell

我可以在javascript中写出如下代码:

function sum(num1, num2) {
  return num1 + num2;
}

然后得到一个值

var someNum = sum(2,5);

我想在 Powershell 中做同样的事情,但我阅读了 following guide:

PowerShell also knows the return keyword; however, it follows a different logic. In general, the purpose of return is to end the execution of a code section and to give the control back to the parent block.

If you add a parameter to the return statement, the value will indeed be returned to the calling subroutine. However, this also applies for all other statements with an output. This means that any output produced in the function will be stored in the variable together with the return parameter.

我想这样做是为了拥有纯功能。但是,它似乎在做

var someNum = sum(2,5);

完全是多余的,当我可以调用上面的函数时,在其中定义someNum,它将在全局范围内可用。

我是不是遗漏了什么或者是否可以在 Powershell 中编写纯函数而不是 return 函数中的所有内容?


有点离题,但这是我的实际代码:

function GetPreviousKeyMD5Hashes() {
    $query = "SELECT Name, MD5, executed FROM [AMagicDb].[dbo].cr_Scripts";

    $command = New-Object System.Data.SQLClient.SQLCommand;
    $command.Connection = $connection;
    $command.CommandText = $query;

    try {
        $reader = $command.ExecuteReader();
        while ($reader.Read()) {
            $key = $reader.GetString(1)
            $previousScripts.Add($key) | Out-Null
        }

        $reader.Close();
        Write-Output "$(Get-Date) Finished querying previous scripts"
    }
    catch {
        $exceptionMessage = $_.Exception.Message;
        Write-Output "$(Get-Date) Error running SQL at with exception $exceptionMessage"
    }
}

然后:

$previousScripts = New-Object Collections.Generic.HashSet[string];
GetPreviousKeyMD5Hashes;

我根本不清楚这段代码 - 运行 GetPreviousKeyMD5Hashes 确实设置了 $previousScripts,但在我之后修改它的人完全不清楚。我唯一的其他选择 (afaik) 是将所有这些都排成一行,这也是不可读的。

Am I missing something or is it possible to write pure functions in Powershell that don't return everything inside the function?

蛋糕不能吃也不能吃...

如果您的函数中没有输出,那么它就是您想要的 "pure"。如果你有输出,那也会成为 return.

的一部分

您可以使用 [ref] 参数。见下文示例。

function DoStuff([ref]$refObj)
{
    Write-Output "DoStuff: Enter"
    $refObj.Value += $(1 + 2)
    $refObj.Value += "more strings"
    Write-Output "DoStuff: Exit"
}

$refRet = @()
$allRet = DoStuff([ref]$refRet)

"allRet"
$allRet
"refRet"
$refRet

"`n`nagain"
$allRet = DoStuff([ref]$refRet)

"allRet"
$allRet
"refRet"
$refRet

is entirely redundant, when I can just call the function above, define someNum inside of it, and it will be available in the global scope.

否:函数在 child 作用域 中执行(除非你用 . 点源它们),所以变量在函数内部创建或分配给它的 local

Am I missing something or is it possible to write pure functions in Powershell that don't return everything inside the function?

是:隐式输出行为仅适用于输出既不是 captured - $var = ... - 也不是 redirected[ 的语句=53=] - ... > foo.txt

如果有语句恰好产生了 输出,您希望丢弃,使用$null = ...... > $null

注意:... | Out-Null 原则上也可以工作,但通常性能会更差,尤其是在早期的 PowerShell 版本中 - 谢谢,TheIncorrigible1.

如果有状态消息你想写而不让它们成为输出的一部分,使用 Write-Host 或者,最好是 Write-Verbose 或者,在 PSv5+ 中,Write-Information,但请注意,后两者需要选择加入才能在控制台中显示它们的输出。
不要使用Write-Output写入状态消息,因为它写入成功输出流,其目的是输出数据("return values").
有关 PowerShell 输出流的更多信息,请参阅我的

因此,您的 JavaScript 代码相当于:

function sum($num1, $num2) {
  Write-Host "Adding $num1 and $num2..."  # print status message to host (console)
  $num1 + $num2 # perform the addition and implicitly output result
}

PS> $someNum = sum 1 2 # NOTE: arguments are whitespace-separated, without (...)
Adding 1 and 2...  # Write-Host output was passed through to console

PS> $someNum  # $someNum captured the success output stream of sum()
3

注意:Powershell 不需要在每条语句的末尾使用分号;仅用于分隔同一行上的多个语句。

只要有可能,最好避免在函数内更改全局状态。将输入作为参数传递,并将输出 return 作为参数传递,因此您不必只以一种方式使用该函数。您的示例可能如下所示:

function sum
{
    param($num1,$num2)
    return $num1+$num2
}

$somenum=sum 2 5

现在,使用 Powershell,不需要 return 语句。没有以其他方式分配、捕获、重定向或以其他方式使用的每个语句的结果,只是与 return 值一起被抛出。所以我们可以用

替换上面的 return 语句
$num1+$num2

您已经在您的代码中使用了它:

$previousScripts.Add($key) | Out-Null

您要舍弃 .Add() 的结果。否则它将包含在 return 值中。

就我个人而言,我发现使用 return 明确标记 return 值更易于阅读。 Powershell 将 all if 输出放在 return 中的方式给我学习带来了很多麻烦。

因此,我对您的代码所做的唯一修复是:

$previousScripts = New-Object Collections.Generic.HashSet[string] 移动到函数内部,使其成为本地函数。

在函数末尾添加return $previousScripts