Linux中shell脚本使用函数

来源:互联网 发布:连锁店会员软件 编辑:程序博客网 时间:2024/06/05 11:08

本书目前为止所有脚本都是从头到尾执行。这样做很好,但你也许已经注意到有些脚本
段间互相重复。
s h e l l允许将一组命令集或语句形成一个可用块,这些块称为s h e l l函数。
本章内容有:

QUOTE:

• 定义函数。
• 在脚本中使用函数。
• 在函数文件中使用函数。
• 函数举例。



函数由两部分组成:
函数标题。
函数体。
标题是函数名。函数体是函数内的命令集合。标题名应该唯一;如果不是,将会混淆结
果,因为脚本在查看调用脚本前将首先搜索函数调用相应的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语句,这取决于使用者。

19.1 在脚本中定义函数
以下是一个简单函数

1. hello()

2. {

3.         echo "Hello there today's date is `date`"

4. } 

复制代码

所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至s h e l l解释器
首次发现它时,才可以使用。调用函数仅使用其函数名即可。上面的例子中,函数名为h e l l o
函数体包含一个e c h o语句,反馈当天日期。

19.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

复制代码

上面例子中,函数定义于脚本顶部。可以在脚本中使用函数名h e l l o调用它。函数执行后,
控制返回函数调用的下一条语句,即反馈语句back from the function。

19.3 向函数传递参数
向函数传递参数就像在一般脚本中使用特殊变量$ 1 , $ 2 . . . $ 9一样,函数取得所传参数后,
将原始参数传回s h e l l脚本,因此最好先在函数内重新设置变量保存所传的参数。这样如果函
数有一点错误,就可以通过已经本地化的变量名迅速加以跟踪。函数里调用参数(变量)的
转换以下划线开始,后加变量名,如: _ F I L E N A M E_ f i l e n a m e\

19.4 从调用函数中返回
当函数完成处理或希望函数基于某一测试语句返回时,可做两种处理:
1) 让函数正常执行到函数末尾,然后返回脚本中调用函数的控制部分。
2) 使用r e t u r n返回脚本中函数调用的下一条语句,可以带返回值。0为无错误,1为有错误。
这是可选的,与最后状态命令报表例子极其类似。其格式为:
r e t u r n 从函数中返回, 用最后状态命令决定返回值。
Return 0 无错误返回。
Return 1 有错误返回\

19.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 

复制代码

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

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

复制代码

如果函数将从测试结果中反馈输出,那么使用替换命令可保存结果。函数调用的替换格
式为:
v a r i a b l e n a m e = f u n c t i o n n a m e
函数f u n c t i o n n a m e输出被设置到变量v a r i a b l e n a m e中。
不久我们会接触到许多不同的函数及使用函数的返回值和输出的不同方法。

9.6 在shell中使用函数
当你收集一些经常使用的函数时,可以将之放入函数文件中并将文件载入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命令。

__________________________________

9.6 在shell中使用函数
当你收集一些经常使用的函数时,可以将之放入函数文件中并将文件载入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命令。

__________________________________

19.8 定位文件
定位文件格式为:
. / 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。如果仍有错误,则应该仔
细检查是否键入了完整路径名。

19.9 检查载入函数
使用s e t命令确保函数已载入。s e t命令将在s h e l l中显示所有的载入函数。
# set

QUOTE:

Linux中似乎没有这个命令。也就是使用set无法看到载入的函数。不知道什么原因?

__________________________________

19.10 执行shell函数
要执行函数,简单地键入函数名即可。这里是带有一个参数的f i n d i t函数,参数是某个系
统文件。
$ findit groups

19.10.1 删除shell函数
现在对函数做一些改动。首先删除函数,使其对s h e l l不可利用。使用u n s e t命令完成此功
能。删除函数时u n s e t命令格式为:
unset function_name
$ unset findit
如果现在键入s e t命令,函数将不再显示。

19.10.2 编辑shell函数
编辑函数f u n c t i o n s . m a i n,加入f o r循环以便脚本可以从命令行中读取多个参数。改动后函
数脚本如下:

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.         for loop

12.         do      

13.                 find / -name $loop -print

14.         done    

15. } 

复制代码

再次定位函数
$. /functions.main.sh
使用s e t命令查看其是否被载入,可以发现s h e l l正确解释f o r循环以接受所有输入参数。

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.         for loop "$@"

12.         do      

13.                 find / -name $loop -print

14.         done    

15. } 

复制代码

现在执行改动过的f i n d i t函数,输入两个参数:
$ findit LPSO.doc passwd

QUOTE:

对应在Linux的默认bash中,我们应该在脚本的某处使用函数名字调用它,而不是是否看它载入。





19.10.3 函数举例
既然已经学习了函数的基本用法,现在就用它来做一些工作。函数可以节省大量的编程
时间,因为它是可重用的。
1. 变量输入
以下脚本询问名,然后是姓。
echo -n "What is your first name :"
read F_NAME
echo -n "What is your surname :"
read $S_NAME

要求输入字符必须只包含字母。如果不用函数实现这一点,要写大量脚本。使用函数可
以将重复脚本删去。这里用a w k语言测试字符。以下是取得只有小写或大写字符的测试函数。

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为一有意义的名字,然后用a w k测试整个传送记录只包含字母,此命令输
出(1为非字母,空为成功)保存在变量_ L E T T E R S O N LY中。
然后执行变量测试,如果为空,则为成功,如果有值,则为错误。基于此项测试,返回
码然后被执行。在对脚本的函数调用部分进行测试时,使用返回值会使脚本清晰易懂。
使用i f语句格式测试函数功能:

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    

复制代码

注意每个输入的w h i l e循环,这将确保不断提示输入直至为正确值,然后跳出循环。当然,
实际脚本拥有允许用户退出循环的选项,可使用适当的游标,正像控制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问题
e c h o语句的使用类型依赖于使用的系统是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"

复制代码

__________________________________

4. 测试目录存在
拷贝文件时,测试目录是否存在是常见的工作之一。以下函数测试传递给函数的文件名
是否是一个目录。因为此函数返回时带有成功或失败取值,可用i f语句测试结果。
函数如下:

1. is_it_a_directory()

2. {

3.         # is_it_a_directory

4.         # to call: is_it_a_directory directory_name

5.         if [ $# -lt 1 ]; then

6.                 echo "is_it_a_directory: I need an argument"

7.                 return 1

8.         fi      

9.         # is it a directory ?

10.         _DIRECTORY_NAME=$1

11.         if [ ! -d $_DIRECTORY_NAME ]; then

12.                 # no it is not

13.                 return 1

14.         else    

15.                 # yes it is

16.                 return 0

17.         fi      

18. }       

复制代码

要调用函数并测试结果,可以使用:

1. echo -n "Enter destination directory :"

2. read DIREC

3. if is_it_a_directory $dires; 

4. then :  

5. else    

6.         echo "$DIREC does no exist, create it now ? [y..]"

7.         # commands go here to either create the directory or exit

8.         ...

9.         ...

10. fi 

复制代码

5. 提示YN
许多脚本在继续处理前会发出提示。大约可以提示以下动作:
• 创建一个目录。
• 是否删除文件。
• 是否后台运行。
• 确认保存记录。
等等
以下函数是一个真正的提示函数,提供了显示信息及缺省回答方式。缺省回答即用户按
下回车键时采取的动作。c a s e语句用于捕获回答。

1. continue_prompt()

2. # continue_prompt

3. # to call: continue_prompt "string to display" default_answer

4. {

5.         _STR=$1

6.         _DEFAULT=$2

7.         # check we have the right params

8.         if [ $# -lt 1 ]; then

9.                 echo "continue_prompt: I need a string to display"

10.                 return 1

11.         fi      

12.         # loop forever

13.         while :

14.         do      

15.                 echo -n "$_STR [Y..N] [$_DEFAULT]:"

16.                 read _ANS

17.                 # if user hits return set the default and determine the return

18.                 # value, that's s: then a <space> then $

19.                 : ${_ANS:=$_DEFAULT}

20.                 if [ "$_ANS" == "" ]; then

21.                         case $_ANS in

22.                                 Y) return 0;;

23.                                 N) return 1;;

24.                         esac    

25.                 fi      

26.                 # user has selected something

27.                 case $_ANS in

28.                         y|Y|Yes|YES) return 0

29.                         ;;

30.                         n|N|No|NO) return 1

31.                         ;;

32.                         *) echo "Answer either Y or N, default is $_DEFAULT"

33.                         ;;

34.                 esac    

35.                 echo $_ANS

36.         done    

37. }       

复制代码

要调用上述函数,须给出显示信息或参数$ 1,或字符串变量。缺省回答YN方式也必须
指定。
以下是几种函数c o n t i n u e p r o m p t的调用格式。
if continue_prompt "Do you want to delete the var filesystem" "N"; then
        echo "Are you nuts!!"
else    
        echo "Phew ! what a good answer"
fi

在脚本中加入上述语句,给出下列输入:
Do you want to delete the var filesystem [Y..N] [N]:
Phew ! what a good answer
Do you want to delete the var filesystem [Y..N] [N]:y
Are you nuts!!

现在可以看出为什么函数要有指定的缺省回答。
以下是函数调用的另一种方式:
if continue_prompt "Do you really want to print this report" "Y"; then
        lpr report
else :
fi
也可以使用字符串变量$ 1调用此函数:
if continue_prompt $1 "Y"; then
        lpr report
else:   
fi 

6. 从登录I D号中抽取信息
当所在系统很庞大,要和一登录用户通信时,如果忘了用户的全名,这是很讨厌的事。
比如有时你看到用户锁住了一个进程,但是它们的用户I D号对你来说没有意义,因此必须要
grep passwd文件以取得用户全名,然后从中抽取可用信息,向其发信号,让其他用户开锁。
以下函数用于从grep /etc/passwd命令抽取用户全名。
本系统用户全名位于p a s s w d文件域5中,用户的系统可能不是这样,这时必须改变其域号
以匹配p a s s w d文件。
这个函数需要一个或多个用户I D号作为参数。它对密码文件进行g r e p操作。
函数脚本如下:

1. whois()

2. # whois

3. # to call: whois userid

4. {

5.         # check we have the right params

6.         if [ $# -lt 1 ]; then

7.                 echo "whois : need user id's pleas"

8.                 return 1

9.         fi      

10.         for loop

11.         do      

12.                 _USER_NAME=`grep $loop /etc/passwd | awk -F: '{print $4}'`

13.                 if [ "$_USER_NAME" == "" ]; then

14.                         echo "whois: Sorry cannot find $loop"

15.                 else    

16.                         echo "$loop is $_USER_NAME"

17.                 fi      

18.         done    

19. }       

复制代码

__________________________________
==========GNU/Linux FreeBSD==========
GNU’s not Unix, but Unix is a beast; its plural form is Unixen.
My MSN: linwh_wylb63@hotmail.com
Freedom. Friends. Features. First.

软件架构线上讨论(有奖)| ChinaUnix活动列表 积分好礼等你拿! | H3C WiNet有奖问答挑战赛| 英特尔嵌入产品市场有奖调查 

L_kernel 

精灵使

  

帖子

2984 

主题

100 

精华

可用积分

23600  

专家积分

0 (本版:0)

来自

Guangzhou 

在线时间

603 小时 

注册时间

2010-04-05 

最后登录

2011-11-04 

状态:...当前离线...

[微博] [博客] [短信] 

[报告] [回复] [引用] 

12楼 发表于 2010-11-26 09:40 | 只看该作者 

本帖最后由 L_kernel 于 2010-11-26 09:41 编辑 

7. 列出文本文件行号
v i编辑器中,可以列出行号来进行调试,但是如果打印几个带有行号的文件,必须使用
n l命令。以下函数用n l命令列出文件行号。原始文件中并不带有行号。

1. # number_file

2. # to call: number_file filename

3. number_file()

4. {

5.         _FILENAME=$1

6.         # check we have the right params

7.         if [ $# -ne 1 ]; then

8.                 echo "number_file: I need a filename to number"

9.                 return 1

10.         fi      

11.         loop=1

12.         while read LINE[quote]

13.         do      

14.                 echo "$loop: $LINE"

15.                 loop=`expr $loop + 1`

16.         done < $_FILENAME

17. } 

复制代码

要调用n u m b e r f i l e函数,可用一个文件名做参数,或在s h e l l中提供一文件名,例如:

1. $ number_file myfile

复制代码

也可以在脚本中这样写或用:

1. number_file $1

复制代码

QUOTE:

Linux中写一个bash脚本,然后在脚本中定义函数。同时使用number_file $1的方式调用该函数。



代码如下:

1. #!/bin/bash

2. # number_file

3. # to call: number_file filename

4. number_file()

5. {

6.         _FILENAME=$1

7.         # check we have the right params

8.         if [ $# -ne 1 ]; then

9.                 echo "number_file: I need a filename to number"

10.                 return 1

11.         fi

12.         loop=1

13.         while read LINE

14.         do

15.                 echo "$loop: $LINE"

16.                 loop=`expr $loop + 1`

17.         done < $_FILENAME

18. }

19. number_file $1

复制代码

输出如下:

1. 1: 10.0.0.1 dev ppp0  proto kernel  scope link  src 192.168.100.7

2. 2: 10.0.0.1 dev ppp1  proto kernel  scope link  src 192.168.100.8

3. 3: 88.88.88.0/24 dev eth0  proto kernel  scope link  src 88.88.88.210

4. 4: 127.0.0.0/24 dev lo  scope link

5. 5: default

6. 6: nexthop dev ppp0 weight 1

7. 7: nexthop dev ppp1 weight 1

8. 8: 

复制代码

打印了输出行。

直接用nl的输出结果如下,对比两者,似乎前者更加直观的。

1.      1  10.0.0.1 dev ppp0  proto kernel  scope link  src 192.168.100.7

2.      2  10.0.0.1 dev ppp1  proto kernel  scope link  src 192.168.100.8

3.      3  88.88.88.0/24 dev eth0  proto kernel  scope link  src 88.88.88.210

4.      4  127.0.0.0/24 dev lo  scope link

5.      5  default

6.      6          nexthop dev ppp0 weight 1

7.      7                  nexthop dev ppp1 weight 1

复制代码

8. 字符串大写
有时需要在文件中将字符串转为大写,例如在文件系统中只用大写字符创建目录或在有
效的文本域中将输入转换为大写数据。
以下是相应功能函数,可以想像要用到t r命令:

1. #/bin/bash

2. # str_to_upper

3. # to call: str_to_upper $1

4. str_to_upper()

5. {

6.         _STR=$1

7.         # check we have the right params

8.         if [ $# -ne 1 ]; then

9.                 echo "number_file: I need a string to convert please"

10.                 return 1

11.         fi      

12.         echo $@ | tr '[a-z]' '[A-Z]'

13. }

14. str_to_upper $1

复制代码

变量u p p e r 保存返回的大写字符串,注意这里用到特定参数$ @来传递所有参数。
s t r t o u p p e r可以以两种方式调用。在脚本中可以这样指定字符串。

1. UPPER=`sh str_to_upper.sh filename`

2. echo $UPPER

复制代码

或者以函数输入参数$ 1的形式调用它。

1. UPPER=`sh str_to_upper.sh $1`

2. echo $UPPER

复制代码

两种方法均可用替换操作以取得函数返回值。

9. is_upper
虽然函数s t r t o u p p e r做字符串转换,但有时在进一步处理前只需知道字符串是否为大写。
i s u p p e r实现此功能。在脚本中使用i f语句决定传递的字符串是否为大写。
函数如下:

1. is_upper()

2. {

3.         # check we have the right params

4.         if [ $# -ne 1 ]; then

5.                 echo "is_upper: I need a string to test OK"

6.                 return 1

7.         fi      

8.         # use awk to check we have only upper case

9.         _IS_UPPER=`echo $1 | awk '{if($0~/[^A-Z]/) print "1"}'`

10.         if [ "$_IS_UPPER" != "" ]; then

11.                 # no, they are not all upper case

12.                 return 1

13.         else    

14.                 # yes all upper case

15.                 return 0

16.         fi      

17. } 

复制代码

要调用i s u p p e r,只需给出字符串参数。以下为其调用方式:

1. echo -n "Enter the filename :"

2. read FILENAME

3. if is_upper $FILENAME; then

4.         echo "Great it's upper case"

5.         # let's create a file maybe ??

6. else    

7.         echo "Sorry it's not upper case"

8.         # shall we convert it anyway using str_to_upper ???

9. fi     

复制代码

要测试字符串是否为小写,只需在函数i s _ u p p e r中替换相应的a w k语句即可。此为
i s l o w e r

1. _IS_LOWER=`echo $1 | awk '{if($0~/[^a-z]/) print "1"}'`

复制代码

__________________________________

10. 字符串小写
现在实现此功能,因为已经给出了s t r t o u p p e r,最好相应给出s t r t o l o w e r。函数工作方
式与前面一样。
函数如下:
str_to_lower()
# str_to_lower
# to call: str_to_lower $1
{
        # check we have the right params
        if [ $# -ne 1 ]; then
                echo "str_to_lower: I need a string to convert please"
                return 1
        fi      
        echo $@ | tr '[A-Z]' '[a-z]'

变量L O W E R保存最近返回的小写字符串。注意用到特定参数$ @ 传递所有参数。
s t r t o l o w e r调用方式也分为两种。可以在脚本中给出字符串:

1. LOWER=`str_to_lower filename`

2. echo $LOWER

复制代码

或在函数中用参数代替字符串:

1. LOWER=`str_to_lower $1`

2. echo $LOWER

复制代码

11. 字符串长度
在脚本中确认域输入有效是常见的任务之一。确认有效包括许多方式,如输入是否为数
字或字符;域的格式与长度是否为确定形式或值。
假定脚本要求用户交互输入数据到名称域,你会想控制此域包含字符数目,比如人名最
多为2 0个字符。有可能用户输入超过5 0个字符。以下函数实施控制功能。需要向函数传递两
个参数,实际字符串和字符串最大长度。
函数如下:

1. check_length()

2. # check length

3. # to call: check_length string max_length_of_string

4. {

5.         _STR=$1

6.         _MAX=$2

7.         # check we have the right params

8.         if [ $# -ne 2 ]; then

9.                 echo "check_length: I need a string and max length the string sh

10. oudle be"

11.                 return 1

12.         fi      

13.         # check the length of the string

14.         _LENGTH=`echo $_STR | awk '{print length($0)}'`

15.         if [ "$_LENGTH" -gt "$_MAX" ]; then

16.                 # length of string is too big

17.                 return 1

18.         else    

19.                 # string is ok in length

20.                 return 0

21.         fi     

复制代码

调用函数c h e c k l e n g t h:

1. while :

2. do

3.         echo -n "Enter your FIRST name :"

4.         read NAME

5.         if check_length $NAME 10

6.         then

7.                 break

8.                 # do nothing fall through condition all is ok

9.         else

10.                 echo "The name field is too long 10 characters max"

11.         fi

12. done

复制代码

循环持续直到输入到变量N A M E的数据小于最大字符长度,这里指定为1 0,b r e a k命令然
后跳出循环。
使用上述脚本段,输出结果如下:

1. Enter your FIRST name :Perterrrrrrrrrrrrrrrrrrrrrrrrrr

2. The name field is too long 10 characters max

3. Enter your FIRST name :Peter

复制代码

可以使用w c命令取得字符串长度。但是要注意,使用w c命令接受键盘输入时有一个误操
作。如果用户输入了一个名字后,点击了几次空格键, w c会将这些空格也作为字符串的一部
分,因而给出其错误长度。a w k在读取键盘时缺省截去字符串末尾处空格。
以下是w c命令的缺点举例(也可以称为特征之一)

1. #!/bin/bash

2. echo -n "name :"

3. read NAME

4. echo $NAME | wc -c

复制代码

运行上述脚本(其中□为空格)
name :Peter♢♢
6

12. chop
c h o p函数删除字符串前面字符。可以指定从第一个字符起删去的字符数。假定有字符串
M Y D O C U M E N T. D O C,要删去M Y D U C U M E N T部分,以便函数只返回. D O C,需要把下述命
令传给c h o p函数:
M Y D O C U M E N T . D O C 1 0
Chop 函数如下:

1. chop()

2. # chop

3. # to call: chop string how_many_chars_to_chop

4. {

5.         _STR=$1

6.         _CHOP=$2

7.         # awk's substr starts at 0, we need to increment by one

8.         CHOP=`expr $_CHOP + 1`

9.         # check we have the right params

10.         if [ $# -ne 2 ]; then

11.                 echo "check_length: I need a sting and how many characters to ch

12. op"

13.                 return 1

14.         fi      

15.         # check the length of the string first

16.         # we can't chop more than what's  in the string !!

17.         _LENGTH=`echo $_STR | awk '{print length($0)}'`

18.         if [ "$_LENGTH" -lt "$_CHOP" ]; then

19.                 echo "Sorry you have asked to chop characters than there are in

20.                 the string"

21.                 return 1

22.         fi      

23.         echo $_STR | awk '{print substr($1,'$_CHOP')}'

24. }       

复制代码

删除后字符串保存于变量C H O P P E D中,使用下面方法调用c h o p函数:

1. CHOPPED=`chop "Honeysuckle" 5`

2. echo $CHOPPED

3. suckle

4. 

5. echo -n "Enter the filename :"

6. read FILENAME

7. CHOPPED=`chop $FILENAME 1`

8. # the first character would be chopped off !

复制代码

13. MONTHS
产生报表或创建屏幕显示时,为方便起见有时要快速显示完整月份。函数m o n t h s,接受
月份数字或月份缩写作为参数,返回完整月份。
例如,传递3或者0 3可返回M a r c h。函数如下:

1. moths()

2. {

3.         # months

4.         _MONTH=$1

5.         # check we have the right params

6.         if [ $# -ne 1 ]; then

7.                 echo "months: I need a number 1 to 12"

8.                 return 1

9.         fi      

10.         case $_MONTH in

11.                 1|01|Jan) _FULL="January" ;;

12.                 2|02|Feb) _FULL="February" ;;

13.                 3|03|Mar) _FULL="March" ;;

14.                 4|04|Apr) _FULL="April" ;;

15.                 5|05|May) _FULL="May" ;;

16.                 6|06|Jun) _FULL="June" ;;

17.                 7|07|Jul) _FULL="July" ;;

18.                 8|08|Aug) _FULL="August" ;;

19.                 9|09|Set|Sept) _FULL="September" ;;

20.                 10|Oct) _FULL="October" ;;

21.                 11|Nov) _FULL="November" ;;

22.                 12|Dec) _FULL="December" ;;

23.                 *) echo "months: Unknown month"

24.                 return 1

25.                 ;;

26.         esac    

27.         echo $_FULL

28. }

复制代码

用下面方法调用函数m o n t h s
months 04
上面例子显示A p r i l,脚本中使用:

1. months 04

2. MY_MONTH=`months 06`

3. echo "Generating the Report for month End $MY_MONTH"

4. ...

复制代码

返回月份J u n e。

__________________________________

19.11 函数调用
这个问题,在前面的地方就有涉及了。
本章最后讲述使用函数的两种不同方法:从原文件中调用函数和使用脚本中的函数。
19.11.1 在脚本中调用函数
要在脚本中调用函数,首先创建函数,并确保它位于调用之前。以下脚本使用了两个函
数。此脚本前面提到过,它用于测试目录是否存在。

1. #!/bin/sh

2. # function file

3. is_it_a_directory()

4. {

5.         # is_it_a_directory

6.         # to call: is_it_a_directory directory_name

7.         _DIRECTORY_NAME=$1

8.         if [ $# -lt 1 ]; then

9.                 echo "is_it_a_directory: I need a directory name to check"

10.                 return 1

11.         fi      

12.         # is it a directory ?

13.         if [ ! -d $_DIRECTORY_NAME ]; then

14.                 return 1

15.         else    

16.                 return 0

17.         fi      

18. }

19. # --------------------------------------------

20. error_msg()

21. {

22.         # error_msg

23.         # beeps: display messages; beeps again!

24.         echo -e "\007"

25.         echo $@

26.         echo -e "\007"

27.         return 0

28. }

29. ### END OF FUNCTIONS

30. echo -n "Enter destination directory :"

31. read DIREC

32. if is_it_a_directory $DIREC

33. then :

34. else    

35.         error_msg "$DIREC does not exist...creating it now"

36.         mkdir $DIREC > /dev/null 2>&1

37.         if [ $? != 0 ]; then

38.                 error_msg "Could not create directory:: check it out!"

39.                 exit 1

40.         else :

41.         fi      

42. fi      

43. # not a directory

44. echo "extracting files ..."

复制代码

上述脚本中,两个函数定义于脚本开始部分,并在脚本主体中调用。所有函数都应该在
任何脚本主体前定义。注意错误信息语句,这里使用函数e r r o r m s g显示错误,反馈所有传递
到该函数的参数,并加两声警报。

19.11.2 从函数文件中调用函数
前面讲述了怎样在命令行中调用函数,这类函数通常用于系统报表功能。
现在再次使用上面的函数,但是这次将之放入函数文件f u n c t i o n s . s h里。s h意即s h e l l脚本。

1. #!/bin/sh

2. # functions.sh

3. # main scripts functions

4. is_it_a_directory()

5. {

6.         # is_it_a_directory

7.         # to call: is_it_a_directory directory_name

8.         _DIRECTORY_NAME=$1

9.         if [ $# -lt 1 ]; then

10.                 echo "is_it_a_directory: I need a directory name to check"

11.                 return 1

12.         fi      

13.         # is it a directory ?

14.         if [ ! -d $_DIRECTORY_NAME ]; then

15.                 return 1

16.         else    

17.                 return 0

18.         fi      

19. }

20. # --------------------------------------------

21. error_msg()

22. {

23.         # error_msg

24.         # beeps: display messages; beeps again!

25.         echo -e "\007"

26.         echo $@

27.         echo -e "\007"

28.         return 0

29. }

复制代码

现在编写脚本就可以调用f u n c t i o n s . s h中的函数了。注意函数文件在脚本中以下述命令格
式定位:
.\<path to file>
使用这种方法不会创建另一个s h e l l,所有函数均在当前s h e l l下执行。

1. #!/bin/sh

2. # direc_check

3. # source the funtion file fuctions.sh

4. # that's a <dot><space><forward slash>

5. . ./functions.sh

6. # now we can use the fuctions(s)

7. echo -n "Enter destination directory :"

8. read DIREC                      

9. if is_it_a_directory $DIREC

10. then :                                  

11. else                                            

12.         error_msg "$DIREC does not exist ... creating it now!"          

13.         mkdir $DIREC > /dev/null 2>&1

14.         if [ $? != 0 ]; then                                                

15.                 error_msg "Could not create directory:: check it out!"

16.                 exit 1

17.         else :  

18.         fi

19. fi                              

20. # not a directory

21. echo "extracting files ..."

复制代码

执行结果如下所示:

1. # sh direc_check.sh

2. Enter destination directory :AUDIT

3. 

4. AUDIT does not exist...creating it now

5. 

6. extracting files ...

复制代码

__________________________________

原创粉丝点击