Явный возврат в Powershell

Я могу написать на javascript следующий код:

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

а затем получить значение

var someNum = sum(2,5);

Я хотел бы сделать то же самое в Powershell, но я прочитал следующее руководство:

PowerShell также знает ключевое слово return; однако это следует другой логике. Обычно цель возврата - завершить выполнение раздела кода и вернуть управление родительскому блоку.

Если вы добавите параметр в оператор return, значение действительно будет возвращено вызывающей подпрограмме. Однако это также относится ко всем остальным операторам с выходными данными. Это означает, что любой вывод, произведенный в функции, будет сохранен в переменной вместе с возвращаемым параметром.

Я хочу сделать это ради чистых функций. Однако, похоже, делает

var someNum = sum(2,5);

полностью избыточен, когда я могу просто вызвать функцию, указанную выше, определить someNum внутри нее, и она будет доступна в глобальной области.

Я что-то упустил или можно написать в Powershell чистые функции, которые не возвращают все внутри функции?


Немного косвенно, но вот мой реальный код:

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) - иметь все это в строке, которая также не читается.


person VSO    schedule 13.08.2018    source источник


Ответы (3)


полностью избыточен, когда я могу просто вызвать функцию, указанную выше, определить внутри нее someNum, и она будет доступна в глобальной области.

Нет: функции выполняются в дочерней области (если вы не указали для них точку с .), поэтому переменные, созданные или назначенные внутри функции, являются локальными к нему.

Я что-то упустил или можно написать в Powershell чистые функции, которые не возвращают все внутри функции?

Да: неявное поведение вывода применяется только к операторам, вывод которых не захвачен - $var = ... - и перенаправлен - ... > foo.txt

Если есть инструкции, которые дают результат, который вы хотите отбросить, используйте $null = ... или ... > $null

Примечание: ... | Out-Null в принципе тоже работает, но обычно будет работать хуже, особенно в более ранних версиях PowerShell - спасибо, TheIncorrigible1 .

Если есть сообщения о состоянии, которые вы хотели бы написать , чтобы они не становились частью вывода, используйте Write-Host или, желательно Write-Verbose, или, в PSv5 +, Write-Information < / strong>, однако обратите внимание, что последние два требуют согласия, чтобы их вывод был виден в консоли.
НЕ используйте Write-Output для написания сообщений о состоянии, так как он записывает в успешный вывод поток, предназначенный для вывода данных ("возвращаемых значений").
См. этот ответ для получения дополнительной информации о потоках вывода 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
person mklement0    schedule 13.08.2018

Я что-то упустил или можно написать в Powershell чистые функции, которые не возвращают все внутри функции?

Ты не можешь съесть свой торт и съесть его ...

Если у вас нет выхода из своей функции, тогда она «чиста», как вы того желаете. Если у вас есть выход, он также становится частью возврата.

Вы можете использовать [ref] params. См., Например, ниже.

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
person Kory Gill    schedule 13.08.2018
comment
Это помогает, не думал о параметрах ref. Я поиграю с этим. - person VSO; 13.08.2018

Примечание. Powershell не требует точки с запятой в конце каждого оператора; только для разделения нескольких операторов в одной строке.

По возможности рекомендуется избегать изменения глобального состояния внутри функции. Передайте ввод как параметры и верните вывод, чтобы вы не были привязаны к использованию функции только одним способом. Ваш образец может выглядеть так:

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

$somenum=sum 2 5

Теперь с Powershell оператор return не нужен. Результат каждого оператора, который не был назначен, захвачен, перенаправлен или иным образом не использован, просто добавляется вместе с возвращаемым значением. Таким образом, мы могли бы заменить приведенный выше оператор return просто

$num1+$num2

Вы уже используете это в своем коде с помощью:

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

где вы отбрасываете результат .Add(). В противном случае он будет включен в возвращаемое значение.

Лично я считаю, что использование return для явной пометки возвращаемого значения облегчает чтение. Способ Powershell помещает все, если результат в возврате вызывает у меня много проблем, когда я учился.

Итак, единственные исправления в вашем коде, которые я бы внес:

Переместите $previousScripts = New-Object Collections.Generic.HashSet[string] внутрь функции, сделав ее локальной.

Добавьте return $previousScripts в конец функции.

person toastifer    schedule 13.08.2018