Возвращаемое значение функции в PowerShell

Я разработал функцию PowerShell, которая выполняет ряд действий, связанных с обеспечением SharePoint сайтов команды. В конечном счете, я хочу, чтобы функция возвращала URL-адрес подготовленного сайта как String, поэтому в конце моей функции у меня есть следующий код:

$rs = $url.ToString();
return $rs;

Код, вызывающий эту функцию, выглядит так:

$returnURL = MyFunction -param 1 ...

Итак, я ожидаю String, но это не так. Вместо этого это объект типа System.Management.Automation.PSMethod. Почему он возвращает этот тип вместо типа String?

Ответ 1

PowerShell имеет действительно дурацкую семантику возврата - по крайней мере, если смотреть с более традиционной перспективы программирования. Есть две основные идеи, которые можно обернуть вокруг:

  • Весь вывод захватывается и возвращается
  • Ключевое слово return на самом деле просто указывает логическую точку выхода

Таким образом, следующие два блока script будут эффективно выполнять то же самое:

$a = "Hello, World"
return $a

$a = "Hello, World"
$a
return

Переменная $a во втором примере оставлена ​​как вывод на конвейере, и, как уже упоминалось, возвращается весь вывод. Фактически, во втором примере вы можете полностью опустить возврат, и вы получите такое же поведение (возврат будет подразумеваться по мере того, как функция будет естественным образом завершена и завершена).

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

Также стоит отметить, что вам, вероятно, не нужны эти точки с запятой, если вы не вставляете несколько выражений в одну строку.

Вы можете больше узнать о семантике возврата на странице about_Return на TechNet или вызвать команду help return из самой PowerShell.

Ответ 2

Эта часть PowerShell, вероятно, самый глупый аспект. Любые посторонние результаты, генерируемые во время функции, будут загрязнять результат. Иногда нет выходных данных, а затем в некоторых условиях есть и другие незапланированные выходные данные, помимо запланированного возвращаемого значения.

Итак, я удаляю назначение из исходного вызова функции, чтобы вывод выводился на экран, а затем пошагово, пока что-то, что я не планировал, не появилось в окне отладчика (с помощью PowerShell ISE).

Даже такие вещи, как резервирование переменных во внешних областях, вызывают вывод, например [boolean]$isEnabled, который досадно выплевывает False, если вы не сделаете это [boolean]$isEnabled = $false.

Другим хорошим примером является $someCollection.Add("thing"), который выплевывает счет новой коллекции.

Ответ 3

С 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. Класс является своего рода гарантией того, что он будет возвращать только объявленный или недействительный тип.

Ответ 4

В качестве обходного пути я возвращал последний объект в массиве, который вы получили из функции... Это не отличное решение, но лучше, чем ничего:

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

$b = someFunction
$b = $b[($b.count - 1)]  # Or
$b = $b[-1]              # Simpler

Ответ 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

Вы можете увидеть реальный пример использования в моем проекте GitHub unpackTunes.

Ответ 6

Трудно сказать, не глядя на код. Убедитесь, что ваша функция не возвращает более одного объекта и что вы фиксируете результаты других вызовов. Что вы получаете за:

@($returnURL).count

В любом случае, два предложения:

Передача объекта в строку:

...
return [string]$rs

Или просто заключите его в двойные кавычки, такие же, как указано выше, но короче, чтобы напечатать:

...
return "$rs"

Ответ 7

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

Чтобы обойти эту проблему, я использовал глобальные переменные, а не возвращал и использовал значение из вызова функции.

Вот еще один вопрос об использовании глобальных переменных: Установка глобальной переменной PowerShell из функции, где имя глобальной переменной является переменной, переданной функции

Ответ 8

Следующий просто возвращает 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".

Ответ 9

В качестве альтернативы return я бы предложил Write-Output.

Write-Output может принимать входные данные из конвейера и пересылать их либо по конвейеру, либо выводить их на консоль, если это последняя команда.

Однако Write-Output не завершает сценарий, как это сделал бы return, что может быть преимуществом, если вывод следует использовать, пересылать и т.д. В цикле.