设为首页 收藏本站
查看: 1421|回复: 0

Powershell学习笔记——函数和函数库

[复制链接]

尚未签到

发表于 2018-9-3 09:26:04 | 显示全部楼层 |阅读模式
  前段时间公司封闭开发,就在封闭的前一天感冒发烧,为了封闭,一顿猛药下去,烧是退了,却在扁桃附近爆发出来——扁桃发炎加溃疡,搞了十多天才好啊,天天喝稀饭啊……所以请大家原谅这么久没有续上学习笔记。顺便:过两天继续封闭,所以这个笔记更新速度可能不会很快了,我尽力。

函数
  函数是Powershell里一个非常重要的东西,与CMD比较起来,这绝对是一个亮点。CMD中只能用“标签”+CALL来模拟函数,而Powershell不仅支持函数,还支持3种类型的函数:普通函数(Function)、过滤器(Filter)和管道函数(Pipeline Function)。除此之外,Powershell的参数解析也是非常智能和强大——当然,参数形式的约定是必不可少的部分。
  函数可以为一系列的操作提供一个快捷命令,而且还可以通过参数来改变函数内的流程或运算结果。还是先来一个示例:
  


  • PS C:\Users\james> f:
  • PS F:\> function cd~ { cd c:\users\james }
  • PS F:\> cd~
  • PS C:\users\james>
  

  这里定义了一个名为“cd~”的函数,目的是直接回到用户目录,就像Linux的“cd ~”一样。这里function是定义函数的关键字,cd~是函数名,{}中的部分则是函数体。——这里还没用到参数,这个,后面再说。执行“cd~”,实际是执行了函数体里面的内容。
  当然,如果一个函数比较复杂,要一行写完是比较痛苦的。那么也可以分多行来写,比如
  


  • PS C:\users\james> function cd~ {
  • >> cd c:\users\james
  • >> }
  • >>
  • PS C:\users\james>
  

  当然,这样写不太方便,因为你在写到最后一行时如果发现前面有错,连修改的机会都没有。不过,如果把一段程序或者函数定义写在脚本文件中,再来执行脚本就会方便得多了——我想,关于脚本文件,在前面已经说过,这里就不用多说了。

函数的参数
  为了演示参数,我们用另一个例子。这个例子会将参数转为大写输出:
  


  • PS F:\> function toUpper { $args[0].toUpper() }
  • PS F:\> toUpper "james"
  • JAMES
  • PS F:\>
  

  这个示例中,用到了未命名参数数组$args。只要是没有命名的参数都会按顺便存在在这个数组中。如果是多个未命名参数,我们可以用用一个循环来依次处理。比如:
  


  • PS F:\> function toUpper {
  • >> foreach ($a in $args) { $a.toUpper() }
  • >> }
  • >>
  • PS F:\> toupper hello james fancy
  • HELLO
  • JAMES
  • FANCY
  • PS F:\>
  

  循环是控制流程的内容,之前的笔记还没提到。这篇笔记主要是记函数,所以也暂时略过不说。

命名参数
  不过这里有一个问题却不得不说——未命名参数。顾名思义,未命名参数就是没有名字的参数;而且,既然有未命名参数,就一定有命名参数。那么命名参数又是什么呢?还是看例子(这次的例子是写的脚本文件):
  


  • # sample.ps1

  • # 命名参数示例

  • function hello($name, $isMale) {
  •      if ([bool]::parse($isMale)) {
  •          "Hello Mr. $name"
  •      } else {
  •          "Hello Ms. $name"
  •      }
  • }

  • hello James true # 参数值为按顺便赋予命名参数
  • hello -ismale false Jenny # 指定了名称的参数值会赋给对应的命名参数,其它的按顺序赋给其它命名参数
  

  运行结果如下:
  


  • PS F:\james\Desktop> .\sample.ps1
  • Hello Mr. James
  • Hello Ms. Jenny
  

多余的参数
  上面的示例中有两个调用函数的示例。第一个没有为参数值指定名称,那么函数调用时会按顺序把参数值赋给参数列表中的命名参数;而第二种调用,为ismale参数指定了值,那么会先将指定了名称的参数值赋给相应的命名参数,其它的参数值再按顺序赋予其它命名参数。现在有一个问题:如果赋予所有命名参数之后还有参数传入,这些参数是否可以通过$args来访问呢?继续做实验,把上面的示例稍做改动:
  


  • # sample.ps1

  • # 多余的参数示例

  • function hello($name, $isMale) {
  •     if ([bool]::parse($isMale)) {
  •         "Hello Mr. $name"
  •     } else {
  •         "Hello Ms. $name"
  •     }

  •     foreach ($a in $args) {
  •        "MORE ARG: $a"
  •     }
  • }

  • hello James true a b c d e f g

  

  运行结果
  


  • PS F:\james\Desktop> .\sample.ps1
  • Hello Mr. James
  • MORE ARG: a
  • MORE ARG: b
  • MORE ARG: c
  • MORE ARG: d
  • MORE ARG: e
  • MORE ARG: f
  • MORE ARG: g
  

默认参数值
  其实多做做实验,答案都是显而易见的。继续下一个问题:“[bool]::parse($isMale)”能不能简化?[bool]::parse其实是调用了System.Boolean的静态方法Parse来将字符串解析为布尔类型的值。如果我们直接传入布尔类型的参数不就可以简化了么?就像这样
  


  • # sample.ps1

  • function hello($name, $isMale) {
  •      if ($isMale) {
  •          "Hello Mr. $name"
  •      } else {
  •          "Hello Ms. $name"
  •      }
  • }

  • hello James $true
  • hello -ismale $false Jenny
  

  其实,还可以更简单,比如对于男士,省略第2个参数……当然,现在这个脚本不行,试试就知道了,可以省略$isMale参数的是女士。再做点改动:
  


  • # sample.ps1

  • # 默认参数值示例

  • function hello($name, $isMale=$true) {
  •      if ($isMale) {
  •          "Hello Mr. $name"
  •      } else {
  •          "Hello Ms. $name"
  •      }
  • }

  • hello James

  • # Hello Mr. James
  

开关[switch]参数
  既然isMale是一个布尔,在控制台脚本里,会很容易让人想起“开关”,即通过一个参数是否存在来表示两种不同的参数值。比如,如果加了“-ismale”参数,则表示男士,否则表示女士——哦,应该换一下,因为不加参数为默认形式,而我们前面约定的默认情况是男士,所以开关参数应该改为“-isFemale”,不过既然是开关,那“is”也可以省了,就是“-female”。示例:
  


  • # sample.ps1
  • # [switch]参数值示例
  • function hello($name, [switch] $female) {
  •     if (!$female) {
  •         "Hello Mr. $name"
  •     } else {
  •         "Hello Ms. $name"
  •     }
  • }

  • hello James
  • hello Jessy -female

  • # Hello Mr. James
  

  有注意到定义参数时指定的[switch]标记么?这叫参数类型。当然[switch]只是参数类型中的一种……

指定参数的类型
  除此之外,还可以为参数指定类型,这样的话,只要给予的参数值不是指定的类型,或者不能转换为指定的类型,就会抛出错误。当然,指定了类型的参数,在函数内进行处理时,往往可以活力掉类型转换的步骤,比如我们想把年、月、日3个参数拼成一个8位长度的日期字符串,下面哪个函数是可以完成呢?
  


  • # sample.ps1

  • # 指定类型的参数示例

  • function add1($a, $b, $c) {
  •      $a + $b + $c
  • }

  • function add2([string]$a, $b, $c) {
  •      $a + $b + $c
  • }

  • add1 2010 10 21 # 输出:2042
  • add2 2010 10 21 # 输出:20111021
  

  由于没有指定类型,add1的三个参数都被当作int型进行处理,相加的结果是2042。而add2中,将$a申明为string类型,虽然$b和$c仍然是被当作int型进行处理,但是“+”遇到不同类型的运算时是自动转为其左边的类型进行运算,所以是字符串相连,结果20111021。

函数的返回值
  Powershell关于函数返回值这个问题,比较复杂。在其它脚本或者语言中,通常来说,通过return之类的关键返回的才是返回值,而Powershell不同,只有是输出到Output的内容,都是返回值,比如
  


  • # sample.ps1
  • function test() {
  •       write-output "Hello"
  •       "James Fancy"
  •       return "OK"
  • }
  • $a = test
  • $a.GetType().FullName
  • $a
  

  它的输出:
  


  • PS F:\james\Desktop> .\sample.ps1
  • System.Object[]
  • Hello
  • James Fancy
  • OK
  

  可以看出来,test函数返回了包含3个值的一个数组,除了最后的return外,前面两个都是写入管道的。哦,管道……这又是一个复杂的东西,后面再来复习。现在只需要记得write-output输出,直接字面值或者变量值输出以及return都会产生返回值就对了。
  很明显,Powershell的函数允许一次返回多个值,这些值都保存在一个数组中。当然,如果函数只返回一个值,那就不需要数组了,比如把上面的示例精简一下:
  


  • # sample.ps1
  • function test() {
  •       "James Fancy"
  • }
  • $a = test
  • $a.GetType().FullName # 输出:System.String
  • $a # 输出:James Fancy

  

Filter,过滤器函数
  除了定义函数之外,也可以定义过滤器。过滤器可以对通过管道进来的内容进行过滤,比如下面这个列子就是为了只列出.exe文件:
  


  • # sample.ps1
  • filter test() {
  •     # 只列出.exe扩展名的文件
  •     if ($_.extension -eq ".exe") { $_ }
  • }

  • dir | test
  

  运行结果:
  

  


  • PS E:\james\Desktop> cd c:/windows
  • PS C:\windows> E:\james\Desktop\sample.ps1

  • 目录: C:\windows


  • Mode LastWriteTime Length Name
  • ---- ------------- ------ ----
  • -a--- 2009-7-14 9:14 65024 bfsvc.exe
  • -a--- 2011-6-9 11:10 642240 bjzq.exe
  • -a--- 2010-1-31 15:23 2614272 explorer.exe
  • -a--- 2009-7-14 9:14 13824 fveupdate.exe
  • -a--- 2009-7-14 9:14 497152 HelpPane.exe
  • -a--- 2009-7-14 9:14 15360 hh.exe
  • -a--- 2011-3-21 23:21 78848 KMSEmulator.exe
  • -a--- 2011-3-21 23:26 151552 KMService.exe
  • -a--- 2009-11-28 7:52 179712 notepad.exe
  • -a--- 2009-7-14 9:14 398336 regedit.exe
  • -a--- 2009-6-11 5:41 49680 twunk_16.exe
  • -a--- 2009-7-14 9:14 31232 twunk_32.exe
  • -a--- 2009-6-11 5:42 256192 winhelp.exe
  • -a--- 2009-7-14 9:14 9728 winhlp32.exe
  • -a--- 2009-7-14 9:14 9216 write.exe
  • -a--- 2011-8-10 12:27 34512 xinstaller.exe


  • PS C:\windows>
  

  注意脚本中最后一句“dir | test”,意思就是把dir的输出通过管道传递给test进行处理。再看test函数的内容,管道中传入的每一项都由特殊变量$_引用。test对传入的每一项都进行判断,将扩展名为.exe的文件对象输出,其余的丢弃。

管道函数
  其实,过滤器是一种特殊的函数,管道函数的简化版。管道函数也是一种特殊的函数,它包含3个部分,begin、process和end。管道输出在进入管道函数的时候,会首先运行begin区域的脚本,仅运行一次;之后从管道进来的每个对象都会经历process过程;所有项结束之后,会触发end区域的脚本。而过滤器就是只定义了process区的管理函数。还是来看例子:
  


  • # sample.ps1
  • function test() {
  •     begin {
  •         "处理开始了"
  •     }
  •     process {
  •         if ($_ -like "a*") { $_ }
  •     }
  •     end {
  •         "处理完成了"
  •     }
  • }

  • # 这次示例用一个数组来演示
  • $("a", "ab", "bac", "b", "bc", "ac") | test
  

  输出:
  


  • PS E:\james\desktop> E:\james\Desktop\sample.ps1
  • 处理开始了
  • a
  • ab
  • ac
  • 处理完成了
  

  管道函数的3个块都可以省略,包括process块,只不过如果省略了process块之后,这个函数就没啥意义了。不过根据实际情况,begin和end块倒是经常被省略的。

函数库
  前面关于运算符的笔记中提到了点号(.)运算符可以用于引入一个脚本,而这个脚本就类似于C/C++中#include的方式被引入到当前位置并执行。那么,如果这个脚本里面只包含函数定义,而不包含其它内容,那么这个脚本就是一个函数库。每次使用该函数库的时候,只需要使用点号运算符引入即可。比如上面的例改稍作改动:
  


  • # sample.ps1
  • function test() {
  •     process {
  •         if ($_ -like "a*") { $_ }
  •     }
  • }
  

  然后在Powershell控制台运行:
  


  • PS E:\james\desktop> . .\sample.ps1
  • PS E:\james\desktop> $("a", "ab", "bac", "b", "bc", "ac") | test
  • a
  • ab
  • ac
  • PS E:\james\desktop>
  

  如果有兴趣试试不使用点号,而是直接运行脚本,那么第二条命令就会出错是,因为test未定义。



运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-561882-1-1.html 上篇帖子: PowerShell V2 高级函数使用举例(03) 下篇帖子: PowerShell 2010: Basic SharePoint Cmdlets
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表