一、简单函数

所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。上面的例子中,函数名为hello,函数体包含一个echo语句,反馈当天日期:
hello() {
        echo "Hello there today's date is `date`"
}

二、在脚本中使用函数

现在创建函数,观察其在脚本中的用法,如下示例:

#!/bin/sh
# func1.sh
hello ()
{
        echo "Hello there today's date is `date`"
}       
echo "now going to the function hello"
hello
echo "back from the function"
执行结果如下:
now going to the function hello
Hello there today's date is Sun Nov 21 23:04:18 HKT 2010
back from the function
上面例子中,函数定义于脚本顶部。可以在脚本中使用函数名hello调用它。函数执行后,控制返回函数调用的下一条语句,即反馈语句back from the function。

三、向函数传递参数

向函数传递参数就像在一般脚本中使用特殊变量$1,$2…$9一样,函数取得所传参数后,将原始参数传回shell脚本,因此最好先在函数内重新设置变量保存所传的参数。这样如果函数有一点错误,就可以通过已经本地化的变量名迅速加以跟踪。函数里调用参数(变量)的转换以下划线开始,后加变量名,如:_FILENAME或_filename。

四、从调用函数中返回

当函数完成处理或希望函数基于某一测试语句返回时,可做两种处理:

1) 让函数正常执行到函数末尾,然后返回脚本中调用函数的控制部分。
2) 使用return返回脚本中函数调用的下一条语句,可以带返回值。0为无错误,1为有错误。这是可选的,与最后状态命令报表例子极其类似。其格式为:

  • Return 0 无错误返回。
  • Return 1 有错误返回

五、函数返回值测试

可以直接在脚本调用函数语句的后面使用最后状态命令来测试函数调用的返回值。例如:
check_it_is_a_directory $FILENAME # this is the function call and check
if [ $? == 0 ] # use the last status command now to test
then    
        echo "All is OK"
else    
        echo "Someting went wrong!"
fi
更好的办法是使用i f语句测试返回0或者返回1。最好在i f语句里用括号将函数调用括起来以增加可读性。例如:
if check_it_is_a_directory $FILENAME; then
        echo "All is OK"
        # do something ??
else    
        echo "Something went wrong !"
        # do something ??
fi
如果函数将从测试结果中反馈输出,那么使用替换命令可保存结果。函数调用的替换格式为:variablename=functioname

六、创建函数文件

下面创建包容函数的函数文件并将之载入shell,进行测试,再做改动,之后再重新载入。函数文件名为functions.main,内容如下:
#!/bin/sh
# functions.main.sh
#
# findit: this is front end for the basic find command
findit() {
        # findit
        if [ $# -lt 1 ]; then
                echo "usage :findit file"
                return 1
        fi      
        find / -name $1 -print
}

#

七、函数举例一

要求输入字符必须只包含字母。如果不用函数实现这一点,要写大量脚本。使用函数可以将重复脚本删去。这里用awk语言测试字符。以下是取得只有小写或大写字符的测试函数。
char_name()
# char_name
# to call: char_name string
# check if $1 does indeed contain only characters a-z,A-Z
{
        # assign the argument across to new variable
        _LETTERS_ONLY=$1
        _LETTERS_ONLY=`echo $1 | awk '{if($0~/[^a-zA-Z]/) print "1"}'`
        if [ "$_LETTERS_ONLY" != "" ]
        then    
                # oops errors
                return 1
        else    
                # contains only chars
                return 0
        fi      
}
首先设置变量$1为一有意义的名字,然后用a w k测试整个传送记录只包含字母,此命令输出(1为非字母,空为成功)保存在变量_LETTERSONLY中。然后执行变量测试,如果为空,则为成功,如果有值,则为错误。基于此项测试,返回码然后被执行。在对脚本的函数调用部分进行测试时,使用返回值会使脚本清晰易懂。使用i f语句格式测试函数功能:
       if char_name $F_NAME
        then    
                # all ok breakout
                break
        else    
                name_error $FNAME
        fi
如果有错误,可编写一个函数将错误反馈到屏幕上:
name_error()
# display an error message
{
        echo " $@ contains errors, it must contain only letters"
}
函数nameerror用于显示所有无效输入错误。使用特殊变量$@显示所有参数,这里为变量FNAME和SNAME值。完成脚本如下:
#!/bin/sh
# func2.sh
char_name()
# char_name
# to call: char_name string
# check if $1 does indeed contain only characters a-z,A-Z
{
        # assign the argument across to new variable
        _LETTERS_ONLY=$1
        _LETTERS_ONLY=`echo $1 | awk '{if($0~/[^a-zA-Z]/) print "1"}'`
        if [ "$_LETTERS_ONLY" != "" ]
        then    
                # oops errors
                return 1
        else    
                # contains only chars
                return 0
        fi      
}       
name_error()
# display an error message
{
        echo " $@ contains errors, it must contain only letters"
}       
while :
do
        echo -n "What is your first name :"
        read F_NAME
        if char_name $F_NAME
        then    
                # all ok breakout
                break
        else    
                name_error $FNAME
        fi      
done    
while :
do
        echo -n "What is your surname :"
        read S_NAME
        if char_name $S_NAME
        then    
                # all ok breakout
                break
        else    
                name_error $S_NAME
        fi      
done
注意每个输入的while循环,这将确保不断提示输入直至为正确值,然后跳出循环。当然,实际脚本拥有允许用户退出循环的选项,可使用适当的游标,正像控制0长度域一样。运行上述脚本的情况如下:
What is your first name :Davi3d
  contains errors, it must contain only letters
What is your first name :David
What is your surname :Tansley1
Tansley1 contains errors, it must contain only letters
What is your surname :Tansley

八、函数举例二

在菜单中进行选择时,最麻烦的工作是必须在选择后键入回车键,或显示“ press any key to continue”。可以使用dd命令解决不键入回车符以发送击键序列的问题。dd命令常用于对磁带或一般的磁带解压任务中出现的数据问题提出质疑或转换,但也可用于创建定长文件。下面创建长度为1兆的文件myfile。

dd if:/dev/zero of=myfile count=512 bs=2048

dd命令可以翻译键盘输入,可被用来接受多个字符。这里如果只要一个字符, dd命令需要删除换行字符,这与用户点击回车键相对应。dd只送回车前一个字符。在输入前必须使用stty命令将终端设置成未加工模式,并在dd执行前保存设置,在dd完成后恢复终端设置。函数如下:
read_a_char()
# read_a_char
{
        # save the settings
        SAVEDSTTY=`stty -g`
        # set terminal raw please
        stty cbreak
        # read and output only one character
        dd if=/dev/tty bs=1 count=1 2> /dev/null
        # retore terminal and restore stty
        stty -cbreak
        stty $SAVEDSTTY
}
要调用函数,返回键入字符,可以使用命令替换操作,例子如下:
echo -n "Hit Any Key To Continue"
character=`read_a_char`echo " In case you are wondering you pressed $character"