PowerShell의 함수 반환 값


188

SharePoint Team 사이트 구축 과 관련된 여러 작업을 수행하는 PowerShell 기능을 개발했습니다 . 궁극적으로 함수가 프로비저닝 된 사이트의 URL을 문자열로 반환하기를 원하므로 함수 끝에서 다음 코드를 갖습니다.

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

이 함수를 호출하는 코드는 다음과 같습니다.

$returnURL = MyFunction -param 1 ...

그래서 String을 기대하지만 그렇지 않습니다. 대신 System.Management.Automation.PSMethod 유형의 개체입니다. 왜 문자열 유형 대신 해당 유형을 반환합니까?


2
함수 선언을 볼 수 있습니까?
zdan

1
아니요, 함수 호출이 아니라 "MyFunction"을 선언 한 방법 / 위치를 보여주십시오. 수행 할 때 수행되는 작업 : Get-Item function : \ MyFunction
zdan

1
이것을 해결하는 다른 방법을 제안했습니다 : stackoverflow.com/a/42842865/992301
Feru

이것이 함수 / 방법과 관련된 스택 오버플로에 대한 가장 중요한 PowerShell 질문 중 하나라고 생각합니다. PowerShell 스크립팅을 배우려는 경우이 전체 페이지를 읽고 철저히 이해해야합니다. 당신이하지 않으면 나중에 자신을 증오합니다.
Birdman 2016 년

답변:


271

PowerShell은 적어도 더 전통적인 프로그래밍 관점에서 볼 때 실제로 이상한 반환 의미를 가지고 있습니다. 머리를 감싸는 두 가지 주요 아이디어가 있습니다.

  • 모든 출력이 캡처되어 반환됩니다
  • return 키워드는 실제로 논리적 종료점을 나타냅니다.

따라서 다음 두 스크립트 블록은 정확히 동일한 기능을 수행합니다.

$a = "Hello, World"
return $a

 

$a = "Hello, World"
$a
return

두 번째 예의 $ a 변수는 파이프 라인의 출력으로 유지되며 언급 된 것처럼 모든 출력이 반환됩니다. 실제로 두 번째 예에서는 리턴을 완전히 생략하고 동일한 동작을 수행 할 수 있습니다 (기능이 자연스럽게 완료되고 종료됨에 따라 리턴이 암시됩니다).

더 많은 함수 정의가 없으면 PSMethod 객체를 얻는 이유를 말할 수 없습니다. 내 생각에는 캡처되지 않고 출력 파이프 라인에 배치되는 몇 줄이있을 것입니다.

또한 한 줄에 여러 표현식을 중첩하지 않는 한 세미콜론이 필요하지 않을 수도 있습니다.

TechNet 의 about_Return 페이지에서 또는 help returnPowerShell 자체 에서 명령을 호출하여 리턴 시맨틱에 대해 자세히 읽을 수 있습니다 .


13
함수에서 스케일러 값을 반환하는 방법이 있습니까?
ChiliYago

10
함수가 해시 테이블을 반환하면 결과를 그대로 처리하지만 다른 명령의 출력을 포함하여 함수 본문 내에서 "에코"하면 갑자기 결과가 혼합됩니다.
Luke Puplett

5
함수 결과의 일부로 해당 텍스트를 반환하지 않고 함수 내에서 화면에 내용을 출력하려면 대신 write-debug변수 $DebugPreference = "Continue"를 설정 한 후 명령 을 사용하십시오."SilentlyContinue"
BeowulfNode42

7
이것은 도움이되었지만이 stackoverflow.com/questions/22663848/… 은 더 많은 도움 이 되었습니다. "... | Out-Null"을 통해 호출 된 함수에서 원하지 않는 명령 / 함수 호출 결과를 파이프 한 다음 원하는 것을 반환합니다.
Straff

1
@LukePuplett echo이 저의 문제였습니다! 그 작은 요점은 매우 중요합니다. 나는 다른 모든 것을 따라 해시 테이블을 반환했지만 여전히 반환 값은 내가 기대 한 것이 아닙니다. 를 제거하고 echo매력처럼 작동했습니다.
Ayo I

54

PowerShell의이 부분은 아마도 가장 어리석은 부분 일 것입니다. 함수 중에 생성 된 외부 출력은 결과를 오염시킵니다. 때때로 출력이없는 경우가 있습니다. 일부 조건에서는 계획된 반환 값 외에 계획되지 않은 다른 출력이있을 수 있습니다.

따라서 원래 함수 호출에서 할당을 제거하므로 출력이 화면에서 끝난 다음 디버거 창에서 계획하지 않은 것이 튀어 나올 때까지 ( PowerShell ISE 사용 ) 단계별로 진행 합니다.

외부 스코프에 변수를 예약하는 것과 같은 것조차도 출력을 유발 [boolean]$isEnabled합니다 [boolean]$isEnabled = $false.

또 다른 좋은 방법 $someCollection.Add("thing")은 새로운 수집 카운트를 내뿜는 것입니다.


2
명시 적으로 정의 된 값으로 변수를 올바르게 초기화하는 모범 사례 대신 이름을 예약하는 이유는 무엇입니까?
BeowulfNode42

2
그 범위에서 사용되는 모든 변수에 대해 각 범위의 시작 부분에 변수 선언 블록이 있음을 의미합니다. C #을 포함한 모든 언어로 변수를 사용하기 전에 모든 변수를 초기화해야합니다. 또한 [boolean]$apowershell에서 중요한 것은 실제로 변수 $ a를 선언하지 않습니다. 해당 라인을 라인으로 따라 $a.gettype()가서이를 확인하고 문자열로 선언하고 초기화 할 때 해당 출력을 비교할 수 $a = [boolean]$false있습니다.
BeowulfNode42

3
당신은 아마 맞습니다. 우연한 쓰기를 방지하기 위해보다 명확한 디자인을 선호합니다.
Luke Puplett

4
프로그래머의 관점에서 볼 때, 나는 PS-n00b이지만 PS 구문의 많은 성가신 측면 중에서 이것이 최악인지는 확실하지 않습니다. 지구상에서 "x> y"대신 "x -gt y"를 쓰는 것이 더 좋다고 생각한 사람은 누구입니까?!? 적어도 내장 연산자가보다 인간 친화적 인 구문을 사용했을 수 있습니까?
The Dag

5
@TheDag는 쉘이기 때문에 프로그래밍 언어는 두 번째입니다. >파일과의 <I / O 스트림 리디렉션에 이미 사용됩니다. >=stdout이라는 파일에 씁니다 =.
TessellatingHeckler

38

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 인 유형 만 반환한다는 보장과 같습니다.


6
노트. 또한 static메소드 를 지원 합니다. 구문으로 이어지는[test_class]::return_what()
Sancarn

1
"이것이 함수라면, 예상되는 결과는 'Hello World \ n808979"가 될 것입니다. 그것이 맞지 않다고 생각합니까? 출력 값은 항상 마지막 문장의 값입니다 (경우에 return 808979따라 function 여부).
amn

1
@amn 감사합니다! 예제에서 함수 내에서 출력을 만든 경우에만 해당 예제를 반환하는 것이 매우 정확합니다. 어느 것이 나를 귀찮게합니다. 때때로 함수가 출력을 생성 할 수 있으며 그 출력과 return 문에 추가 한 값이 반환됩니다. 아시다시피 클래스는 선언되거나 void 인 유형 만 반환합니다. 함수를 사용하는 경우 하나의 쉬운 방법은 함수를 변수에 할당하는 것입니다. 반환 값보다 많은 값이 반환되면 var는 배열이고 반환 값은 배열의 마지막 항목이됩니다.
DaSmokeDog

1
라는 명령에서 무엇을 기대 Write-Output하십니까? 여기에서 언급되는 "콘솔"출력이 아니라 함수 / cmdlet의 출력입니다. 콘솔에 물건을 덤프하려는 경우가 있습니다 Write-Verbose, Write-Host그리고 Write-Error다른 사람의 사이에서 기능. 기본적으로 콘솔 출력 절차보다 의미에 Write-Output더 가깝습니다 return. 그것을 남용하지 말고, Write-Verbose자세한 정보를 위해 사용하십시오.
amn

1
@ amn 나는 이것이 콘솔이 아니라는 것을 잘 알고 있습니다. 함수 대 클래스 내부에서 반환이 예기치 않은 결과를 처리하는 방법을 보여주는 빠른 방법이었습니다. 함수에서 모든 출력 + 반환 값은 모두 다시 전달됩니다. 그러나 클래스 반환에 지정된 값만 전달됩니다. 직접 실행 해보십시오.
DaSmokeDog

31

해결 방법으로 함수에서 돌아온 배열의 마지막 객체를 반환했습니다 ... 훌륭한 솔루션은 아니지만 아무것도 아닌 것보다 낫습니다.

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]

7
$b = $b[-1]마지막 요소 또는 배열을 얻는 가장 간단한 방법이지만 원하지 않는 값을 출력하지 않는 것이 더 간단합니다.
던컨

@ Duncan 그리고 함수에 물건을 출력하고 싶다면 동시에 반환 값을 원한다면 어떻게해야합니까?
Utkan Gezer 2016

@ThoAppelsin 터미널에 내용을 쓰려면 write-host직접 출력하십시오.
던컨

7

콘솔에 출력하고 싶을 때 리턴 크래쉬를 피하기 위해 단일 결과 멤버와 함께 간단한 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 에서 실제 예제 사용법을 볼 수 있습니다 .


4

코드를 보지 않고 말하기는 어렵습니다. 함수가 둘 이상의 객체를 반환하지 않아야하며 다른 호출에서 수행 된 결과를 캡처해야합니다. 무엇을 얻습니까?

@($returnURL).count

어쨌든, 두 가지 제안 :

객체를 문자열로 캐스팅하십시오.

...
return [string]$rs

또는 위와 동일하지만 입력하기에는 짧은 큰 따옴표로 묶습니다.

...
return "$rs"

1
또는 심지어 "$rs"- return함수에서 일찍 돌아올 때만 필요합니다. 반품을 빼면 PowerShell 관용구가 더 좋습니다.
David Clarke

4

이러한 시나리오에서 함수 결과대한 Luke의 설명은 올바른 것으로 보입니다. 근본 원인 만 이해하고 싶습니다. PowerShell 제품 팀은이 동작에 대해 무언가를 수행 할 것입니다. 꽤 일반적이며 디버깅 시간이 너무 많이 걸립니다.

이 문제를 해결하기 위해 함수 호출에서 값을 반환하고 사용하는 대신 전역 변수를 사용했습니다.

전역 변수 사용에 대한 또 다른 질문이 있습니다 . 전역 변수 이름이 함수에 전달 된 변수 인 함수에서 전역 PowerShell 변수 설정


3

다음은 단순히 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"이 무엇인지 모르는 오류를 반환합니다.


2

기존 답변은 정확하지만 때로는 a Write-Output또는 a로 명시 적으로 무언가를 반환하지는 않지만 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-NullNew-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().

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.