介绍很多时候,我们写的代码不是只运行一次就不再使用,所以需要保存在一个文件中。我们通常将包含解释性编程语言代码的可执行文件称为脚本文件,或简称为脚本。在脚本内部,也会有一些可以重复使用的代码。我们可以把这样的代码写成一个函数,供其他部分调用。Zsh中的函数和脚本基本相同,一个脚本可以看成是一个函数,文件名就是函数名。脚本和函数的写法基本相同,就放在一起说吧。首先从功能开始,因为涉及的细节较少。函数定义#一个很简单的functionfun(){echogood}#也可以在前面加一个函数关键字functionfun(){echogood}这样就可以定义一个函数了。括号必须是空的,即使函数有参数,也没有必要在里面写参数列表。只需输入函数名称即可调用该函数。fun(){echogood}%fungood使用unfunction删除函数。fun(){echogood}%unfunctionfun%funzsh:commandnotfound:fun参数处理函数可以有参数,但是zsh中不需要明确说明有多少个参数,直接读取即可。fun(){echo$1$2$3echo$#}%funaaaa1%funaabbccaabbcc3%funaabbccddaabbcc4$n为第n个参数,$#为参数个数。如果读取时没有传入相应的参数,则与读取一个未定义变量的效果相同。函数的参数只能是字符串类型。如果将整数和浮点数传递给函数,它们也会被转换为字符串。可以将数组传递给函数,数组中的元素将依次成为参数。fun(){echo$1$2$3echo$#}%array=(112233)%fun$array1122333这样使用的好处是可以更方便的处理带空格的参数。#遍历所有参数,$*是包含所有参数的数组fun(){fori($*){echo$i}}%funabcabc可以通过$+n快速判断第n个参数是否存在。fun(){(($+1))&&{echo$1}}在$\*和$@上。在bash中,$\*和$@的区别是一件比较麻烦的事情,但是在zsh中,一般不需要用到$@,这个坑就别踩了。Bash中之所以需要使用$@,是因为如果使用$\*,参数中有空格,很难分清参数中哪些是空格,哪些是参数之间的间隔符($*中的)bash是一个字符串)。如果使用“$\*”,则所有参数合并为一个字符串。而"$@"可以保留参数中的空格,所以通常使用"$@"。但是有时候需要把所有的参数拼接成一个字符串,然后用“$*”,这样就很费解了。zsh中的$\*会在参数中包含空格(zsh中的$*是一个数组),所以效果类似于bash的“$@”。同样在zsh中使用"$*"与在bash中使用"$\*"具有相同的效果,因此只需使用$*和"$*"就足够了。函数嵌套函数可以嵌套定义。fun(){fun2(){echo$2}fun2$1$2}%funaabbbbfun2函数是在fun执行后才定义的,但是最外层也可以直接访问fun2函数。如果想让最外面的部分不可访问,可以调用unfunctionfun2在fun结束前删除fun2函数。返回值函数需要返回一个表示函数是否正确执行的返回值。如果为0,说明执行正确。如果不为0,则表示有错误。#!/bin/zshfun(){(($+1))&&{return}return1}%fun111&&echogoodgood%fun||echobadbad%fun#你也可以用$?获取函数的返回值%echo$?遇到return后,函数立即结束。return是返回0。注意返回值不是用来返回数据的。如果函数需要返回字符串、整数、浮点数等给调用者,只需使用echo或print等命令输出,然后调用者用$(fun)即可获取。如果需要返回数组或者哈希表,只能通过变量(全局变量或者函数级别的局部变量)传递。fun(){echo123.456}%echo$($(fun)*2))246.91200000000001通过全局变量返回。array=()fun(){array=(aabb)}%fun%echo$arrayaabb函数内的局部变量可以直接读写函数外的变量,函数内定义的新变量在函数退出。str1=abcdfun(){echo$str1str2=1234}%funabcd%echo$str21234这通常不是预期的。为了防止函数中的变量“渗”出函数,可以使用局部变量,使用local来定义变量。str1=abcdfun(){echo$str1localstr2=1234}%funabcd%echo$str2函数中的变量,除非真的需要保留给外部使用,否则最好全部使用局部变量,以免造成bug.脚本可以认为脚本也是一个函数,只是单独写在一个文件中。test.zsh内容。#!/bin/zshechogood这是一个非常简单的脚本文件。第一行固定为系统查找zsh解释器,只需在#!后加上zsh的绝对路径即可。如果需要使用环境变量访问,可以使用#!/bin/envzsh(或!/usr/bin/envzsh,如果env在/usr/bin/中)。从第二行开始,与函数中相同。上面函数体中的内容(去掉第一行和最后一行的fun(){和}可以写在这里。执行时在test.zsh所在目录下运行zshtest.zsh并添加参数(就像调用一个名为zshtest.zsh的函数,也可以用chmodu+xtest.zsh给它加上可执行权限,然后直接带参数运行./test.zsh。参数和返回值的处理方法脚本,和函数的完整性一样,这里就不举例了。但是函数和脚本是有区别的。函数在当前zsh进程中执行(调用时也可以加括号在子进程中执行),而脚本则在新的子进程中执行。子进程立即退出,所以脚本中的变量值无法被外界访问,也不需要使用本地定义(使用没有问题)。exit命令脚本可以使用return返回,也可以使用exit命令。exit命令的用法与return类似,如果不加参数,则返回0。但是在代码的任何地方,调用exit命令都会退出脚本,即使是在深度嵌套的函数中调用也是如此。使用getopts命令处理命令行选项有时候我们写的脚本需要支持比较复杂的命令行选项,比如demo-iaa-tbb-cxcccddd,这种情况下手动处理会很麻烦。您可以使用内置的getopts命令。#!/bin/zsh#i:表示可以接受带参数的-i选项#c表示可以接受不带参数的-c选项while{getoptsi:t:cvarg}{case$arg{(i)#$OPTARG存储对应于选项的参数echo$argoptionwitharg:$OPTARG;;(t)用arg回显$arg选项:$OPTARG;;(c)echo$arg选项;;(v)回显版本:0.1;;(?)echoerrorreturn1;;}}#$OPTIND指向第一个未处理的参数echo$*[$OPTIND,-1]运行结果:%./demo-iaaa-tbbb-cvcccdddioptionwitharg:aaatoptionwitharg:bbbcoptionversion:0.1cccddd#只能加一些选项%./demo-iaaa-vbbbcccioptionwitharg:aaaversion:0.1bbbccc#没有一个选项可以加Add%./demoaaabbbaaabbb#如果选项没有参数,多个选项可以合并为一个-在%./demo-iaaa-cvbbbcccioptionwitharg:aaacoptionversion:0.1bbbccc#如果选项有参数如果选项没有参数,一个将报告错误%./demo-iaaa-tioptionwitharg:aaa./demo:3:argumentexpectedafter-toptionerror#添加不受支持的选项也会报告错误%./demo-iaaa-abbb带有arg的ccci选项:aaa./demo:3:错误选项:-aerror#如果带参数的选项没有参数,然后后面跟着另一个选项,这个选项会被当作参数%./demo-i-caaabbbioptionwitharg:-caaabbbgetopts用起来还是很方便的是的,但它不支持长选项(例如--logaaa)。如果需要使用长选项,可以使用getopt命令,这是一个外部命令,可以通过mangetopt查看其用法。总结本文简单介绍了函数和脚本的编写,重点介绍了参数处理和返回值等,还有很多地方没有涉及到,以后可能会补充。参考https://my.oschina.net/lengli...更新历史20170901:添加使用$?获取函数的返回值。20170902:添加了“使用getopts命令处理命令行选项”。本文不再更新,全系列文章更新维护在这里:github.com/goreliu/zshguideWindows、Linux、Shell、C、C++、AHK、Python、JavaScript、Lua等相关问题的付费解决方案,定价灵活,欢迎咨询,微信ly50247。
