Powershell的函數返回值與其餘腳本語言存在較大差異。shell
第一要搞明白什麼是「默認輸出」,默認輸出就是沒有指定任何特定輸出設備的語句所要輸出到的地方。至關於傳統編程語言或者shell的stdout,其實就叫標準輸出也行。像以下語句:編程
PS C:\Users\Administrator> 1+1
2
PS C:\Users\Administrator>編程語言
1+1是個表達式,它的結果是2,咱們並無指定把結果輸出到哪裏,則系統就丟到「默認輸出」或「標準輸出」裏,默認輸出的行爲就是簡單地把它回顯出來。函數
再如同:.net
PS C:\Users\Administrator> $x="Hello World!"
PS C:\Users\Administrator> $x
Hello World!
PS C:\Users\Administrator>orm
一樣$x變量沒有指定任何左值,因此也是把它丟到標準輸出去,即顯示變量的值。對象
明白了這個再來看Powershell中函數返回值的問題。Powershell的函數返回值是函數體裏面全部輸出的值,而並非return語句指定的那個值。get
也就是說,函數體裏面任何到標準輸出的值都是返回值的一部分。it
好比這個函數:io
function test1()
{
"abc"
}
若是按照傳統編程或腳本的思路來看,這裏沒有任何return語句,理應沒有返回值,但運行這個函數的結果倒是:
PS C:\Users\Administrator> test1
abc
PS C:\Users\Administrator>
按照傳統思路來看這樣的搞法就很糾結了,並且彷佛也沒什麼必要這麼玩,玩起來也會有無數問題,我們一個一個道來。
第一個問題:返回值應該只有1個纔對,按照這樣的方法來搞,豈不是我放幾個變量裏面,就會有幾個返回值嗎?這樣混亂的函數返回,後續應該怎麼處理啊?
答案:確實只有一個返回值,但也確實你輸出什麼他就返回什麼,因此結果呢,就是返回值是一個大Array,Array裏面包含了函數裏全部的輸出。不信看這個函數test2:
function test2()
{
"abc"
"def"
123
}
看這個函數執行的結果:
PS C:\Users\Administrator> test2
abc
def
123
PS C:\Users\Administrator>
咱們還能夠把執行結果保存到變量,並經過調用GetType()方法來得知返回的對象類型:
PS C:\Users\Administrator> $result=test2
PS C:\Users\Administrator> $result.GetType()IsPublic IsSerial Name BaseType
——– ——– —- ——–
True True Object[] System.Array
能夠看到確實是返回了Array。
第二個問題:有辦法指定返回值類型嗎,我還期望經過返回來的數據繼續處理呢,這麼一個大包Array丟過來,後續工做豈不是很麻煩?
答案:一點都不麻煩,其實返回回來的數據,都完整的保存了原有的數據類型,處理起來沒有任何障礙,請看剛纔的test1和test2函數:
PS C:\Users\Administrator> $result=test1
PS C:\Users\Administrator> $result.GetType()IsPublic IsSerial Name BaseType
——– ——– —- ——–
True True String System.ObjectPS C:\Users\Administrator> $result=test2
PS C:\Users\Administrator> $result | % { $_.GetType()}IsPublic IsSerial Name BaseType
——– ——– —- ——–
True True String System.Object
True True String System.Object
True True Int32 System.ValueType
test1只有一個返回項,因此能夠直接GetType(),能夠看到String被傳回來,而test2由於Array裏面有3項,因此遍歷整個Array,能夠看到2個String和1個Int32也都完整保存下來了。其實這裏能夠傳遞任何.net類庫中的object類型甚至是自定義的對象類型,徹底沒有問題。
第三個問題:有些輸出我不想讓它放到返回值怎麼辦?好比下面一個函數:
function test3()
{
$due=1000
Write-Output "您的預付款餘額是:"
Write-Output $due
}
輸出結果天然就是:
PS C:\Users\Administrator> test3
您的預付款餘額是:
1000
若是想把1000從函數結果當初提取出來,非要解開Array取第二個值,那是在太麻煩了,還不如不用函數算了。就像必須這樣來拿數據:
PS C:\Users\Administrator> $x=test3
PS C:\Users\Administrator> $x
您的預付款餘額是:
1000
PS C:\Users\Administrator> $x[1]
1000
這麼搞太挫了,咱們要既容許函數輸出信息,又要讓最終返回只要我須要的值,去掉提示信息,那怎麼辦?
答案:函數這麼寫,把輸出語句用Write-Host代替:
function test3()
{
$due=1000
Write-Host "您的預付款餘額是:"
Write-Output $due
}
再來看函數輸出:
PS C:\Users\Administrator> test3
您的預付款餘額是:
1000
PS C:\Users\Administrator> $x=test3
您的預付款餘額是:
PS C:\Users\Administrator> $x
1000
PS C:\Users\Administrator>
能夠看到提示語句是函數運行時輸出的,但並不包含在返回值當中。其實這裏Write-Host指的就是在屏幕輸出,而Write-Output指的是輸出到標準輸出,固然若是省略Write-Output也是輸出到標準輸出。
第四個問題:有些Cmdlet/Fuction/Method自己具備返回值,咱們有時只是須要執行這個Cmdlet/Function/Method自己而已,並不須要它的返回值,此時返回值會自做聰明的被咱們外層Function捕獲到而又被返回上去,怎麼辦?好比有這麼一個function test4,我但願取得當前目錄,並列一下目錄裏面的文件,這個很簡單:
function test4()
{
pwd
dir
}
可問題來了,pwd返回的是當前目錄的Directory object,dir也返回當前目錄的object,這樣返回值就有了2個當前目錄,而咱們只要一個,怎麼辦?想固然的改爲Write-Host dir,結果也很喜劇,直接輸出三個字母"dir」給你看。
答案:稍微一想就有解決方案了:
function test4()
{
pwd
$temp=dirWrite-Host $temp
}
這都要臨時變量,是否是太傻了點?並且若是這裏不是dir而是一個耗時很長才能完成的一個調用,並有很長輸出的調用,這裏徹底是用了一個一點用處都沒有的變量,確實太傻了。
不過若是不是Cmdlet的話,像一些方法之類的東西,能夠在方法調用時前面加上[void]強令其無返回值,也能夠解決,其實大部分狀況,碰到的應該都是這種類型。爲了說明這個用法須要稍微複雜一點的例子,前面那些愚蠢的用法解釋不了這個問題,我借用了Powershell Cookbook中關於performance counter調用的一個例子,以下:
Function getcounter()
{
$arguments="System","System Up Time"
$counter= New-Object System.Diagnostics.Performancecounter $arguments
[void] $counter.NextValue()
New-Object TimeSpan 0,0,0,$counter.NextValue()}
根據.net類庫,NextValue調用必須使用2次才能獲得正確結果,因此咱們能夠在第一次調用前加一[void]來避免額外的返回值。
第五個問題:我強烈要求繼續使用return關鍵字來限定返回值,有辦法嗎?
答案:很不幸這個沒有解決方案,雖然Powershell支持return關鍵字,但函數的行爲是不會受它影響的。好比:
function test5()
{
"abc"
$x="def"
return $x
}
test5的運行結果以下:
PS C:\Users\Administrator> test5
abc
def
PS C:\Users\Administrator>
仍是將前面的abc以及return中的變量所有輸出,其實這裏的return跟Write-Oupput沒有任何區別。
這就是Powershell中函數一個讓人不怎麼爽的特性,說實話,若是第一次接觸函數,這麼搞應該會挺舒服,可是對於有其餘程序和腳本經驗的人,這個特性就有些蹩腳了。