s h e l l允许将一组命令集或语句形成一个可用块,这些块称为s h e l l函数。
函数体。
标题是函数名。函数体是函数内的命令集合。标题名应该唯一;如果不是,将会混淆结
果,因为脚本在查看调用脚本前将首先搜索函数调用相应的s h e l l。
定义函数的格式为:
函数名()
{
命令1
. . .
}
或者
函数名(){
命令1
. . .
}
两者方式都可行。如果愿意,可在函数名前加上关键字f u n c t i o n,这取决于使用者。
f u n c t i o n 函数名()
{ ...
}
可以将函数看作是脚本中的一段代码,但是有一个主要区别。执行函数时,它保留当前
s h e l l和内存信息。此外如果执行或调用一个脚本文件中的另一段代码,将创建一个单独的
s h e l l,因而去除所有原脚本中定义的存在变量。
函数可以放在同一个文件中作为一段代码,也可以放在只包含函数的单独文件中。函数
不必包含很多语句或命令,甚至可以只包含一个e c h o语句,这取决于使用者。
所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至s h e l l解释器
首次发现它时,才可以使用。调用函数仅使用其函数名即可。
向函数传递参数就像在一般脚本中使用特殊变量$ 1 , $ 2 . . . $ 9一样,函数取得所传参数后,
将原始参数传回s h e l l脚本,因此最好先在函数内重新设置变量保存所传的参数。这样如果函
数有一点错误,就可以通过已经本地化的变量名迅速加以跟踪。函数里调用参数(变量)的
转换以下划线开始,后加变量名,如: _ F I L E N A M E或_ f i l e n a m e。
当函数完成处理或希望函数基于某一测试语句返回时,可做两种处理:
1) 让函数正常执行到函数末尾,然后返回脚本中调用函数的控制部分。
2) 使用r e t u r n返回脚本中函数调用的下一条语句,可以带返回值。0为无错误,1为有错误。
这是可选的,与最后状态命令报表例子极其类似。其格式为:
r e t u r n 从函数中返回, 用最后状态命令决定返回值。
Return 0 无错误返回。
Return 1 有错误返回
可以直接在脚本调用函数语句的后面使用最后状态命令来测试函数调用的返回值。
[root@localhost huangcd]# cat test.txt
#!/bin/bash
hello()
{
echo "hello there today's date is `date`"
}
hello
if [ $?=0 ]
then echo "ok"
else
echo "wrong"
fi
[root@localhost huangcd]# sh test.txt
hello there today's date is 2013年 12月 11日 星期三 09:13:31 CST
ok
当你收集一些经常使用的函数时,可以将之放入函数文件中并将文件载入s h e l l。
文件头应包含语句# ! / b i n / s h,文件名可任意选取,但最好与相关任务有某种实际联系。例
如,f u n c t i o n s . m a i n。
一旦文件载入s h e l l,就可以在命令行或脚本中调用函数。可以使用s e t命令查看所有定义
的函数。输出列表包括已经载入s h e l l的所有函数。
如果要改动函数,首先用u n s e t命令从s h e l l中删除函数,尽管u n s e t删除了函数以便于此函
数对于s h e l l或脚本不可利用,但并不是真正的删除。改动完毕后,再重新载入此文件。有些
s h e l l会识别改动,不必使用u n s e t命令,但为了安全起见,改动函数时最好使用u n s e t命令。
下面创建包容函数的函数文件并将之载入s h e l l,进行测试,再做改动,之后再重新载入。
函数文件名为f u n c t i o n s . m a i n,内容如下:
上述脚本本书前面用过,现在将之转化为一个函数。这是一个基本f i n d命令的前端。如果
不加参数,函数将返回1,即发生错误。注意错误语句中用到了实际函数名,因为这里用$ 0,
s h e l l将只返回s h -信息,原因是文件并不是一个脚本文件。这类信息对用户帮助不大。
[root@localhost huangcd]# cat functions.main
#!/bin/bash
findit(){
if[ $# -lt 1 ]
then echo "usage:findit file"
retrun 1
fi
find / -name $1 -print
}
定位文件格式为:
. / p a t h n a m e / f i l e n a m e
现在文件已经创建好了,要将之载入s h e l l,试键入:
$. functions.main
如果返回信息file not found,再试:
$. /functions.main
此即<点> <空格> <斜线> <文件名>,现在文件应该已载入s h e l l。如果仍有错误,则应该仔
细检查是否键入了完整路径名。
[root@localhost huangcd]# . functions.main
使用s e t命令确保函数已载入。s e t命令将在s h e l l中显示所有的载入函数。
[root@localhost huangcd]# set
BASH=/bin/bash
BASH_ARGC=()
BASH_ARGV=()
BASH_LINENO=()
tmpid=0
findit ()
{
if [ $# -lt 1 ]; then
echo "usage:findit file";
return 1;
fi;
find / -name $1 -print
}
要执行函数,简单地键入函数名即可。这里是带有一个参数的f i n d i t函数,参数是某个系
统文件。
[root@localhost huangcd]# findit huangcd
/home/huangcd
[2]+ Stopped find / -name $1 -print
[root@localhost huangcd]# findit
usage:findit file
现在对函数做一些改动。首先删除函数,使其对s h e l l不可利用。使用u n s e t命令完成此功
能。删除函数时u n s e t命令格式为:unset function_name
[root@localhost huangcd]# unset findit
编辑函数f u n c t i o n s . m a i n,加入f o r循环以便脚本可以从命令行中读取多个参数。改动后函
数脚本如下:
[root@localhost huangcd]# cat functions.main
#!/bin/bash
findit(){
if [ $# -lt 1 ]; then
echo "usage:findit file"
return 1
fi
for loop
do
find / -name $1 -print
done
}
再次定位函数
[root@localhost huangcd]# . functions.main
使用s e t命令查看其是否被载入,可以发现s h e l l正确解释f o r循环以接受所有输入参数。
[root@localhost huangcd]# set
tmpid=0
findit ()
{
if [ $# -lt 1 ]; then
echo "usage:findit file";
return 1;
fi;
for loop in "$@";
do
find / -name $1 -print;
done
}
现在执行改动过的f i n d i t函数,输入两个参数:
[root@localhost huangcd]# findit huangcd
/home/huangcd
/var/spool/mail/huangcd
/var/run/sudo/huangcd
/home/huangcd
/var/spool/mail/huangcd
/var/run/sudo/huangcd
You have new mail in /var/spool/mail/root
既然已经学习了函数的基本用法,现在就用它来做一些工作。函数可以节省大量的编程
时间,因为它是可重用的。
要求输入字符必须只包含字母。如果不用函数实现这一点,要写大量脚本。使用函数可
以将重复脚本删去。这里用a w k语言测试字符。以下是取得只有小写或大写字符的测试函数
首先设置变量$ 1为一有意义的名字,然后用a w k测试整个传送记录只包含字母,此命令输
出(1为非字母,空为成功)保存在变量_ L E T T E R S O N LY中。
然后执行变量测试,如果为空,则为成功,如果有值,则为错误。基于此项测试,返回
码然后被执行。在对脚本的函数调用部分进行测试时,使用返回值会使脚本清晰易懂。
使用i f语句格式测试函数功能:
如果有错误,可编写一个函数将错误反馈到屏幕上。
函数n a m e e r r o r用于显示所有无效输入错误。使用特殊变量$ @显示所有参数,这里为变
量F N A M E和S N A M E值。完成脚本如下:
[root@localhost huangcd]# cat func2
#!/bin/bash
char_name(){
# check if $1 indeed contain only characters a-z,A-Z
_LETTERS_NOLY=$1
_LETTERS_ONLY=`echo $1|awk '{if($0~/[^a-zA-Z]/) print "1"}'`
if[ "$_LETTERS_ONLY" != "" ]
then
return 1
else
return 0
fi
}
name_error()
{
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 break
else
name_error $F_NAME
fi
done
while:
do
echo -n "what is your surname:"
read S_NAME
if char_name $S_NAME
then break
else
name_error $S_NAME
fi
done