PowerShell 函数返回值的问题

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.Object

PS 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=dir

Write-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中函数一个让人不怎么爽的特性,说实话,若是第一次接触函数,这么搞应该会挺舒服,可是对于有其余程序和脚本经验的人,这个特性就有些蹩脚了。

相关文章
相关标签/搜索