Linux Shell 函数

Shell 允许将一组命令集或语句形成一个可用块,这些块称为 Shell 函数.

本章内容有:

· 定义函数

· 在脚本中使用函数

· 在函数文件中使用函数

  · 函数举例

函数由两部分组成:
· 函数标题

· 函数体

标题是函数名。函数体是函数内的命令集合。标题名应该唯一;如果不是,将会混淆结
果,因为脚本在查看调用脚本前将首先搜索函数调用相应的 Shell.

定义函数的格式为:

函数名()
{
命令1
. . .

}

如果愿意,可在函数名前加上关键字 function,这取决于使用者。
function 函数名()

...

}

可以将函数看作是脚本中的一段代码,但是有一个主要区别。执行函数时,它保留当前

Shell 和内存信息。此外如果执行或调用一个脚本文件中的另一段代码,将创建一个单独的

Shell,因而去除所有原脚本中定义的存在变量.

函数可以放在同一个文件中作为一段代码,也可以放在只包含函数的单独文件中。函数

不必包含很多语句或命令,甚至可以只包含一个 echo 语句,这取决于使用者.


1. 在脚本中定义函数

以下是一个简单函数

  1. hello()
  2. {
  3. echo "Hello there today's date is `date`"
  4. }

所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至 Shell 解释器

首次发现它时,才可以使用。调用函数仅使用其函数名即可。上面的例子中,函数名为 hello,

函数体包含一个 echo 语句,返回当天日期.


2. 在脚本中使用函数

现在创建函数,观察其在脚本中的用法。

  1. #!/bin/sh
  2. # func1.sh
  3. hello()
  4. {
  5.         echo "Hello there today's date is `date`"
  6. }       
  7. echo "now going to the function hello"
  8. hello
  9. echo "back from the function"

执行结果如下:

  1. now going to the function hello
  2. Hello there today's date is Sun Nov 21 23:04:18 HKT 2010
  3. back from the function

上面例子中,函数定义于脚本顶部。可以在脚本中使用函数名 hello 调用它。函数执行后,

控制返回函数调用的下一条语句,即返回语句back from the function.


3. 向函数传递参数

向函数传递参数就像在一般脚本中使用特殊变量$1 , $2 . . . $9一样,函数取得所传参数后,

将原始参数传回 Shell 脚本,因此最好先在函数内重新设置变量保存所传的参数。这样如果函

数有一点错误,就可以通过已经本地化的变量名迅速加以跟踪。函数里调用参数(变量)的

转换以下划线开始,后加变量名,如: _FILENAME 或 _filename.


4. 从调用函数中返回

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

1) 让函数正常执行到函数末尾,然后返回脚本中调用函数的控制部分。

2) 使用 return 返回脚本中函数调用的下一条语句,可以带返回值。0为无错误,1为有错误。

这是可选的,与最后状态命令报表例子极其类似。其格式为:

return 从函数中返回, 用最后状态命令决定返回值.

Return 0 无错误返回.

Return 1 有错误返回.


5. 函数返回值测试

可以直接在脚本调用函数语句的后面使用最后状态命令来测试函数调用的返回值。例如:

  1. check_it_is_a_directory $FILENAME #this is the function call and check
  2. if [ $? == 0 ] #use the last status command now to test
  3. then    
  4.         echo "All is OK"
  5. else    
  6.         echo "Someting went wrong!"
  7. fi

更好的办法是使用 if 语句测试返回0或者返回1。最好在 if 语句里用括号将函数调用括起来
以增加可读性
。例如:

  1. if check_it_is_a_directory $FILENAME; then
  2.         echo "All is OK"
  3.         # do something ??
  4. else    
  5.         echo "Something went wrong !"
  6.         # do something ??
  7. fi

如果函数将从测试结果中反馈输出,那么使用替换命令可保存结果。函数调用的替换格
式为:

variablename = functionname

函数 functionname 输出被设置到变量 variablename 中。

不久我们会接触到许多不同的函数及使用函数的返回值和输出的不同方法


6. 在shell中使用函数

当你收集一些经常使用的函数时,可以将之放入函数文件中并将文件载入Shell.

文件头应包含语句 #!/bin/bash,文件名可任意选取,但最好与相关任务有某种实际联系。例如:function.main.

一旦文件载入 Shell,就可以在命令行或脚本中调用函数。可以使用 set 命令查看所有定义

的函数。输出列表包括已经载入 Shell 的所有函数。

如果要改动函数,首先用 unset 命令从 Shell 中删除函数,尽管 unset 删除了函数以便于此函

数对于 Shell 或脚本不可利用,但并不是真正的删除。改动完毕后,再重新载入此文件。有些

Shell 会识别改动,不必使用 unset 命令,但为了安全起见,改动函数时最好使用 unset 命令.


7. 创建函数文件

下面创建包容函数的函数文件并将之载入 Shell,进行测试,再做改动,之后再重新载入.

函数文件名为 function.main,内容如下:

  1. #!/bin/sh
  2. # functions.main.sh
  3. #
  4. # findit: this is front end for the basic find command
  5. findit() {
  6.         # findit
  7.         if [ $# -lt 1 ]; then
  8.                 echo "usage :findit file"
  9.                 return 1
  10.         fi      
  11.         find / -name $1 -print
  12. }  

上述脚本前面用过,现在将之转化为一个函数。这是一个基本 find 命令的前端。如果

不加参数,函数将返回1,即发生错误。注意错误语句中用到了实际函数名,因为这里用 $0,

Shell 将只返回 sh- 信息,原因是文件并不是一个脚本文件。这类信息对用户帮助不大。


8. 定位文件

定位文件格式为:./pathname/filename

现在文件已经创建好了,要将之载入 Shell,试键入:.functions.main

如果返回信息file not found,再试:./functions.main

此即<点> <斜线> <文件名>,现在文件应该已载入 Shell。如果仍有错误,则应该仔

细检查是否键入了完整路径名.


10. 执行shell函数

要执行函数,简单地键入函数名即可。这里是带有一个参数的 findit 函数,参数是某个系统文件.

./findit groups

10.1 函数举例

既然已经学习了函数的基本用法,现在就用它来做一些工作。函数可以节省大量的编程

时间,因为它是可重用的。

1. 变量输入

以下脚本询问名,然后是姓.

echo -n "What is your first name :" 

read F_NAME

echo -n "What is your surname :" 

read S_NAME

要求输入字符必须只包含字母。如果不用函数实现这一点,要写大量脚本。使用函数可

以将重复脚本删去。这里用 awk 语言测试字符。以下是取得只有小写或大写字符的测试函数。

  1. char_name()
  2. # char_name
  3. # to call: char_name string
  4. # check if $1 does indeed contain only characters a-z,A-Z
  5. {
  6.         # assign the argument across to new variable
  7.         _LETTERS_ONLY=$1
  8.         _LETTERS_ONLY=`echo $1 | awk '{if($0~/[^a-zA-Z]/) print "1"}'`
  9.         if [ "$_LETTERS_ONLY" != "" ]
  10.         then    
  11.                 # oops errors
  12.                 return 1
  13.         else    
  14.                 # contains only chars
  15.                 return 0
  16.         fi      
  17. }      

首先设置变量 $1 为一有意义的名字,然后用 awk 测试整个传送记录只包含字母,此命令输

出(1为非字母,空为成功)保存在变量_ L E T T E R S O N LY中。

然后执行变量测试,如果为空,则为成功,如果有值,则为错误。基于此项测试,返回

码然后被执行。在对脚本的函数调用部分进行测试时,使用返回值会使脚本清晰易懂。

使用 if 语句格式测试函数功能:

  1.        if char_name $F_NAME
  2.         then    
  3.                 # all ok breakout
  4.                 break
  5.         else    
  6.                 name_error $FNAME
  7.         fi      

如果有错误,可编写一个函数将错误反馈到屏幕上。

  1. name_error()
  2. # display an error message
  3. {
  4.         echo " $@ contains errors, it must contain only letters"
  5. }      

函数n a m e e r r o r用于显示所有无效输入错误。使用特殊变量 $@ 显示所有参数,这里为变

量F N A M E和S N A M E值。完成脚本如下:

  1. #!/bin/sh
  2. # func2.sh
  3. char_name()
  4. # char_name
  5. # to call: char_name string
  6. # check if $1 does indeed contain only characters a-z,A-Z
  7. {
  8.         # assign the argument across to new variable
  9.         _LETTERS_ONLY=$1
  10.         _LETTERS_ONLY=`echo $1 | awk '{if($0~/[^a-zA-Z]/) print "1"}'`
  11.         if [ "$_LETTERS_ONLY" != "" ]
  12.         then    
  13.                 # oops errors
  14.                 return 1
  15.         else    
  16.                 # contains only chars
  17.                 return 0
  18.         fi      
  19. }       
  20. name_error()
  21. # display an error message
  22. {
  23.         echo " $@ contains errors, it must contain only letters"
  24. }       
  25. while :
  26. do
  27.         echo -n "What is your first name :"
  28.         read F_NAME
  29.         if char_name $F_NAME
  30.         then    
  31.                 # all ok breakout
  32.                 break
  33.         else    
  34.                 name_error $FNAME
  35.         fi      
  36. done    
  37. while :
  38. do
  39.         echo -n "What is your surname :"
  40.         read S_NAME
  41.         if char_name $S_NAME
  42.         then    
  43.                 # all ok breakout
  44.                 break
  45.         else    
  46.                 name_error $S_NAME
  47.         fi      
  48. done   

注意每个输入的 while 循环,这将确保不断提示输入直至为正确值,然后跳出循环。当然,

实际脚本拥有允许用户退出循环的选项,可使用适当的游标,正像控制0长度域一样。

运行上述脚本的情况如下:

  1. What is your first name :Davi3d
  2.   contains errors, it must contain only letters
  3. What is your first name :David
  4. What is your surname :Tansley1
  5. Tansley1 contains errors, it must contain only letters
  6. What is your surname :Tansley

2. echo问题

echo 语句的使用类型依赖于使用的系统是 L I N U X、B S D 还是系统V,本书对此进行了讲解。

下面创建一个函数决定使用哪种e c h o语句。

使用e c h o时,提示应放在语句末尾,以等待从r e a d命令中接受进一步输入。

L I N U X和B S D为此使用e c h o命令- n选项。

以下是L I N U X(B S D)e c h o语句实例,这里提示放于e c h o后面:

$ echo -n "Your name :"

Your name :{{?}}

系统V使用\ C保证在末尾提示:
echo "Your name :\C"
Your name :♢

在e c h o语句开头L I N U X使用- e选项反馈控制字符。其他系统使用反斜线保证s h e l l获知控
制字符的存在。
有两种方法测试e c h o语句类型,下面讲述这两种方法,这样,就可以选择使用其中一个。
第一种方法是在e c h o语句里包含测试控制字符。如果键入\ 0 0 7和一个警铃,表明为系统V,
如果只键入\ 0 0 7,显示为L I N U X。
以下为第一个控制字符测试函数。

  1. uni_prompt()
  2. {
  3.         if [ `echo "\007"` == "\007" ] >/dev/null 2>&1
  4.                 # does a bell sound or are the characters just echoed??
  5.         then    
  6.                 # characters echoed, it's LINUX/BSD
  7.                 echo -e -n "$@"
  8.         else    
  9.                 # it's System V
  10.                 echo "$@\c"
  11.         fi      
  12. }      

注意这里又用到了特殊变量$ @以反馈字符串,要在脚本中调用上述函数,可以使用:
uni_prompt "\007 there goes the bell ,What is your name:"
这将发出警报并反馈‘ What is your name:’,并在行尾显示字符串。如果在末尾出现字
符,则为系统V版本,否则为L I N U X / B S D版本。
第二种方法使用系统V \c测试字母z是否悬于行尾。

  1. uni_prompt()
  2. # uni_prompt
  3. # univeral prompt
  4. {
  5.         if [ `echo "Z\c"` == "Z" ] >/dev/null 2>&1
  6.                 # echo any character out, does it hang on to the end of line ??
  7.         then    
  8.                 # yes, it's System V
  9.                 echo "$@\c"
  10.         else    
  11.                 # No, it's LINUX, BSD
  12.                 echo -e -n "$@"
  13.         fi      
  14. }      

要在脚本中调用上述函数,可以使用:
uni_prompts "\007 there goes the ,What is your name:"
使用两个函数中任意一个,并加入一小段脚本:
uni_prompt "\007 There goes the bell, What is your name :"
read NAME
将产生下列输出:
There goes the be,What is your name:

3. 读单个字符

在菜单中进行选择时,最麻烦的工作是必须在选择后键入回车键,或显示“ press any key
to continue”。可以使用d d命令解决不键入回车符以发送击键序列的问题。
d d命令常用于对磁带或一般的磁带解压任务中出现的数据问题提出质疑或转换,但也可
用于创建定长文件。下面创建长度为1兆的文件m y f i l e。
dd if:/dev/zero of=myfile count=512 bs=2048
d d命令可以翻译键盘输入,可被用来接受多个字符。这里如果只要一个字符, d d命令需
要删除换行字符,这与用户点击回车键相对应。d d只送回车前一个字符。在输入前必须使用
s t t y命令将终端设置成未加工模式,并在d d执行前保存设置,在d d完成后恢复终端设置。
函数如下:

  1. read_a_char()
  2. # read_a_char
  3. {
  4.         # save the settings
  5.         SAVEDSTTY=`stty -g`
  6.         # set terminal raw please
  7.         stty cbreak
  8.         # read and output only one character
  9.         dd if=/dev/tty bs=1 count=1 2> /dev/null
  10.         # retore terminal and restore stty
  11.         stty -cbreak
  12.         stty $SAVEDSTTY
  13. }

要调用函数,返回键入字符,可以使用命令替换操作,例子如下:

  1. echo -n "Hit Any Key To Continue"
  2. character=`read_a_char`
  3. echo " In case you are wondering you pressed $character"



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值