1.1 shell函数
在shell中,函数可以被当作命令一样执行,它是命令的组合结构体。可以将函数看成是一个普通命令或者一个小型脚本。
首先给出几个关于函数的结论:
(1).当在bash中直接调用函数时,如果函数名和命令名相同,则优先执行函数,除非使用command命令。例如:定义了一个名为rm的函数,在bash中输入rm执行时,执行的是rm函数,而非/bin/rm命令,除非使用"command rm ARGS"。
(2).如果函数名和命令别名同名,则优先执行别名。也就是说,在优先级方面:别名>函数>命令自身。
(3).当前shell定义的函数只能在当前shell使用,子shell无法继承父shell的函数定义。除非使用"export -f"将函数导出为全局函数。
(4).定义了函数后,可以使用unset -f移除当前shell中已定义的函数。
(5).除非出现语法错误,或者已经存在一个同名只读函数,否则函数的退出状态码是函数内部结构中最后执行的一个命令的退出状态码。
(6).可以使用typeset -f [func_name]或declare -f [func_name]查看当前shell已定义的函数名和对应的定义语句。使用typeset -F或declare -F则只显示当前shell中已定义的函数名。
(7).函数可以递归,递归层次可以无限。
函数的语法结构:
[ function ] name () compound-cmd [redirection]
上面的语法结构中定义了一个名为name的函数,关键字function是可选的,如果使用了function关键字,则name后的括号可以省略。compound-cmd是函数体,通常使用大括号{}包围,由于历史原因,大括号本身也是关键字,所以为了不产生歧义,函数体必须和大括号使用空格、制表符、换行符分隔开来。还可以指定可选的函数重定向功能,这样当函数被调用的时候,指定的重定向也会被执行。
上面的语法结构中定义了一个名为name的函数:
- 关键字function是可选的,如果使用了function关键字,则name后的括号可以省略。
- compound-cmd是函数体,通常使用大括号{}包围。由于历史原因,大括号本身也是关键字,所以为了不产生歧义,函数体和大括号之间必须使用空格、制表符、换行符分隔开来。
- 同理,大括号中的每一个命令都必须使用分号";"、"&"结束或换行书写。如果使用"&"结束某条命令,这表示该命令放入后台执行。
- 还可以指定可选的函数重定向功能,这样当函数被调用的时候,指定的重定向也会被执行。
例如:定义一个名为rm的函数,该函数会将传递的所有文件移动到"~/backup"目录下,目的是替代rm命令,避免误删除的危险操作。
[root@xuexi ~]# function rm () { [ -d ~/rmbackup ] || mkdir ~/rmbackup;/bin/mv -f $@ ~/rmbackup; } &>/dev/null
在调用rm函数时,只需是给rm函数传递参数即可。例如,要删除/tmp/a.log。
[root@xuexi ~]# rm /tmp/a.log
在执行函数时,会将执行可能输出的信息重定向到/dev/null中。
为了让函数在子shell(例如脚本)中也可以使用,使用export的"-f"选项将其导出为全局函数。取消函数的导出则使用export的"-n"选项。
export -f rm export -n rm
另外需要注意的是,函数支持无限递归。这可能在不经意间出错,导致崩溃。例如,写一个名为"ls"的函数。
function ls() { ls -l; }
这时执行ls命令会卡住,和想象中的"ls -l"效果完全不同,因为函数体中的ls也递归成了函数,这将无限递归下去。
关于shell函数,还有几个需要说明的知识点:
(8).shell函数也接受位置变量$0、$1、$2...,但函数的位置参数是调用函数时传递给函数的,而非传递给脚本的参数。所以脚本的位置变量和函数的位置变量是不同的,但是$0和脚本的位置变量$0是一致的。另外,函数也接受特殊变量"$#",和脚本的"$#"一样,它也表示位置变量的个数。
(9).函数体内部可以使用return命令,当函数结构体中执行到return命令时将退出整个函数。return后可以带一个状态码整数,即return n,表示函数的退出状态码,不给定状态码时默认状态码为0。
(10).函数结构体中可以使用local命令定义本地变量,例如:local i=3。本地变量只在函数内部(包括子函数)可见,函数外不可见。
(11).只有先定义了函数,才可以调用函数。不允许函数调用语句在函数定义语句之前。
1.2 条件结构:if
语法结构:
if test-commands1; then
commands1;
[elif test-commands2; then
commands2;]
...
[else
commands3;]
fi
if的判断很简单,一切都以返回状态码是否为0为判决条件。如果test-commands1执行后的退出状态码为0(不是其执行结果为0),则执行commands1部分的结构体,否则如果test-commands2返回0则执行commands2部分的结构体,如果都不满足,则执行commands3的结构体。
常见的test-commands有几种类型:
(1).一条普通的命令。只要该命令退出状态码为0,则执行then后的语句体。例如:
if echo haha &>/dev/null;then echo go;fi
(2).测试语句。例如test、[]、[[]]。
if [ $((1+2)) -eq 3 ];then echo go;fi if [[ "$name" =~ "long" ]];then echo go;fi<