您的位置:首页 > 运维架构 > Shell

PowerShell探秘(5-1)函数返回值的问题

2013-11-22 03:33 274 查看
文章来源:http://opslife.com/return-value-diffrence-in-powershell/ 如要转载,请说标明文章来源

第一要搞明白什么是“默认输出”,默认输出就是没有指定任何特定输出设备的语句所要输出到的地方。相当于传统编程语言或者shell的stdout,其实就叫标准输出也行。像如下语句:

PS C:\Users\Administrator> 1+1 



PS C:\Users\Administrator>

1+1是个表达式,它的结果是2,我们并没有指定把结果输出到哪里,则系统就丢到“默认输出”或“标准输出”里,默认输出的行为就是简单地把它回显出来。

再如同:

PS C:\Users\Administrator> $x="Hello World!" 

PS C:\Users\Administrator> $x 

Hello World! 

PS C:\Users\Administrator>

同样$x变量没有指定任何左值,所以也是把它丢到标准输出去,即显示变量的值。

明白了这个再来看Powershell中函数返回值的问题。Powershell的函数返回值是函数体里面所有输出的值,而并不是return语句指定的那个值。

也就是说,函数体里面任何到标准输出的值都是返回值的一部分。

比如这个函数:

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中函数一个让人不怎么爽的特性,说实话,如果第一次接触函数,这么搞应该会挺舒服,但是对于有其他程序和脚本经验的人,这个特性就有些蹩脚了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息