拷贝文件时,测试目录是否存在是常见的工作之一。以下函数测试传递给函数的文件名是否是一个目录。因为此函数返回时带有成功或失败取值,可用if语句测试结果。函数如下:
is_it_a_directory() { # is_it_a_directory # to call: is_it_a_directory directory_name if [ $# -lt 1 ]; then echo "is_it_a_directory: I need an argument" return 1 fi # is it a directory ? _DIRECTORY_NAME=$1 if [ ! -d $_DIRECTORY_NAME ]; then # no it is not return 1 else # yes it is return 0 fi }
要调用函数并测试结果,可以使用:
echo -n "Enter destination directory :" read DIREC if is_it_a_directory $dires; then : else echo "$DIREC does no exist, create it now ? [y..]" # commands go here to either create the directory or exit ... ... fi
许多脚本在继续处理前会发出提示。大约可以提示以下动作:
• 创建一个目录。
• 是否删除文件。
• 是否后台运行。
• 确认保存记录。
等等
以下函数是一个真正的提示函数,提供了显示信息及缺省回答方式。缺省回答即用户按下回车键时采取的动作。case语句用于捕获回答:
continue_prompt() # continue_prompt # to call: continue_prompt "string to display" default_answer { _STR=$1 _DEFAULT=$2 # check we have the right params if [ $# -lt 1 ]; then echo "continue_prompt: I need a string to display" return 1 fi # loop forever while : do echo -n "$_STR [Y..N] [$_DEFAULT]:" read _ANS # if user hits return set the default and determine the return # value, that's s: then a <space> then $ : ${_ANS:=$_DEFAULT} if [ "$_ANS" == "" ]; then case $_ANS in Y) return 0;; N) return 1;; esac fi # user has selected something case $_ANS in y|Y|Yes|YES) return 0 ;; n|N|No|NO) return 1 ;; *) echo "Answer either Y or N, default is $_DEFAULT" ;; esac echo $_ANS done }
要调用上述函数,须给出显示信息或参数$1,或字符串变量。缺省回答Y或N方式也必须指定。以下是几种函数continueprompt的调用格式
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
当所在系统很庞大,要和一登录用户通信时,如果忘了用户的全名,这是很讨厌的事。比如有时你看到用户锁住了一个进程,但是它们的用户ID号对你来说没有意义,因此必须要用grep passwd文件以取得用户全名,然后从中抽取可用信息,向其发信号,让其他用户开锁。以下函数用于从grep /etc/passwd命令抽取用户全名。本系统用户全名位于passwd文件域5中,用户的系统可能不是这样,这时必须改变其域号以匹配passwd文件。这个函数需要一个或多个用户ID号作为参数。它对密码文件进行grep操作。函数脚本如下:
whois() # whois # to call: whois userid { # check we have the right params if [ $# -lt 1 ]; then echo "whois : need user id's pleas" return 1 fi for loop do _USER_NAME=`grep $loop /etc/passwd | awk -F: '{print $4}'` if [ "$_USER_NAME" == "" ]; then echo "whois: Sorry cannot find $loop" else echo "$loop is $_USER_NAME" fi done }
在vi编辑器中,可以列出行号来进行调试,但是如果打印几个带有行号的文件,必须使用nl命令。以下函数用nl命令列出文件行号。原始文件中并不带有行号。
# number_file # to call: number_file filename number_file() { _FILENAME=$1 # check we have the right params if [ $# -ne 1 ]; then echo "number_file: I need a filename to number" return 1 fi loop=1 while read LINE[quote] do echo "$loop: $LINE" loop=`expr $loop + 1` done < $_FILENAME }
要调用numberfile函数,可用一个文件名做参数,或在shell中提供一文件名,例如:
$ number_file myfile
也可以在脚本中这样写或用:
number_file $1
在Linux中写一个bash脚本,然后在脚本中定义函数。同时使用number_file $1的方式调用该函数,代码如下:
#!/bin/bash # number_file # to call: number_file filename number_file() { _FILENAME=$1 # check we have the right params if [ $# -ne 1 ]; then echo "number_file: I need a filename to number" return 1 fi loop=1 while read LINE do echo "$loop: $LINE" loop=`expr $loop + 1` done < $_FILENAME } number_file $1
执行脚本输出如下:
1: 10.0.0.1 dev ppp0 proto kernel scope link src 192.168.100.7 2: 10.0.0.1 dev ppp1 proto kernel scope link src 192.168.100.8 3: 88.88.88.0/24 dev eth0 proto kernel scope link src 88.88.88.210 4: 127.0.0.0/24 dev lo scope link 5: default 6: nexthop dev ppp0 weight 1 7: nexthop dev ppp1 weight 1 8:
直接用nl的输出结果如下,对比两者,似乎前者更加直观:
1 10.0.0.1 dev ppp0 proto kernel scope link src 192.168.100.7 2 10.0.0.1 dev ppp1 proto kernel scope link src 192.168.100.8 3 88.88.88.0/24 dev eth0 proto kernel scope link src 88.88.88.210 4 127.0.0.0/24 dev lo scope link 5 default 6 nexthop dev ppp0 weight 1 7 nexthop dev ppp1 weight 1
有时需要在文件中将字符串转为大写,例如在文件系统中只用大写字符创建目录或在有效的文本域中将输入转换为大写数据。以下是相应功能函数,可以想像要用到tr命令:
#/bin/bash # str_to_upper # to call: str_to_upper $1 str_to_upper() { _STR=$1 # check we have the right params if [ $# -ne 1 ]; then echo "number_file: I need a string to convert please" return 1 fi echo $@ | tr '[a-z]' '[A-Z]' } str_to_upper $1
变量upper 保存返回的大写字符串,注意这里用到特定参数$@来传递所有参数。strtoupper可以以两种方式调用。在脚本中可以这样指定字符串。
UPPER=`sh str_to_upper.sh filename` echo $UPPER
或者以函数输入参数$1的形式调用它:
UPPER=`sh str_to_upper.sh $1` echo $UPPER
虽然函数strtoupper做字符串转换,但有时在进一步处理前只需知道字符串是否为大写。isupper实现此功能。在脚本中使用if语句决定传递的字符串是否为大写。函数如下:
is_upper() { # check we have the right params if [ $# -ne 1 ]; then echo "is_upper: I need a string to test OK" return 1 fi # use awk to check we have only upper case _IS_UPPER=`echo $1 | awk '{if($0~/[^A-Z]/) print "1"}'` if [ "$_IS_UPPER" != "" ]; then # no, they are not all upper case return 1 else # yes all upper case return 0 fi }
要调用isupper,只需给出字符串参数。以下为其调用方式:
echo -n "Enter the filename :" read FILENAME if is_upper $FILENAME; then echo "Great it's upper case" # let's create a file maybe ?? else echo "Sorry it's not upper case" # shall we convert it anyway using str_to_upper ??? fi
要测试字符串是否为小写,只需在函数is_upper中替换相应的awk语句即可。此为islower。
_IS_LOWER=`echo $1 | awk '{if($0~/[^a-z]/) print "1"}'`
在 脚本 中确认域输入有效是常见的任务之一。确认有效包括许多方式,如输入是否为数字或字符;域的格式与长度是否为确定形式或值。假定脚本要求用户交互输入数据到名称域,你会想控制此域包含字符数目,比如人名最多为20个字符。有可能用户输入超过50个字符。以下函数实施控制功能。需要向函数传递两个参数,实际字符串和字符串最大长度。函数如下:
check_length() # check length # to call: check_length string max_length_of_string { _STR=$1 _MAX=$2 # check we have the right params if [ $# -ne 2 ]; then echo "check_length: I need a string and max length the string sh oudle be" return 1 fi # check the length of the string _LENGTH=`echo $_STR | awk '{print length($0)}'` if [ "$_LENGTH" -gt "$_MAX" ]; then # length of string is too big return 1 else # string is ok in length return 0 fi }
调用函数checklength:
while : do echo -n "Enter your FIRST name :" read NAME if check_length $NAME 10 then break # do nothing fall through condition all is ok else echo "The name field is too long 10 characters max" fi done
循环持续直到输入到变量NAME的数据小于最大字符长度,这里指定为10,break命令然后跳出循环。使用上述脚本段,输出结果如下:
Enter your FIRST name :Perterrrrrrrrrrrrrrrrrrrrrrrrrr The name field is too long 10 characters max Enter your FIRST name :Peter
可以使用wc命令取得字符串长度。但是要注意,使用wc命令接受键盘输入时有一个误操作。如果用户输入了一个名字后,点击了几次空格键, wc会将这些空格也作为字符串的一部分,因而给出其错误长度。awk在读取键盘时缺省截去字符串末尾处空格。以下是wc命令的缺点举例(也可以称为特征之一)
#!/bin/bash echo -n "name :" read NAME echo $NAME | wc -c
运行上述脚本(其中♢为空格)
name eter♢♢ 6
要在脚本中调用函数,首先创建函数,并确保它位于调用之前。以下脚本使用了两个函数。此脚本前面提到过,它用于测试目录是否存在。
#!/bin/sh # function file is_it_a_directory() { # is_it_a_directory # to call: is_it_a_directory directory_name _DIRECTORY_NAME=$1 if [ $# -lt 1 ]; then echo "is_it_a_directory: I need a directory name to check" return 1 fi # is it a directory ? if [ ! -d $_DIRECTORY_NAME ]; then return 1 else return 0 fi } # -------------------------------------------- error_msg() { # error_msg # beeps: display messages; beeps again! echo -e "/007" echo $@ echo -e "/007" return 0 } ### END OF FUNCTIONS echo -n "Enter destination directory :" read DIREC if is_it_a_directory $DIREC then : else error_msg "$DIREC does not exist...creating it now" mkdir $DIREC > /dev/null 2>&1 if [ $? != 0 ]; then error_msg "Could not create directory:: check it out!" exit 1 else : fi fi # not a directory echo "extracting files ..."
上述脚本中,两个函数定义于脚本开始部分,并在脚本主体中调用。所有函数都应该在任何脚本主体前定义。注意错误信息语句,这里使用函数errormsg显示错误,反馈所有传递到该函数的参数,并加两声警报。
前面讲述了怎样在命令行中调用函数,这类函数通常用于系统报表功能。现在再次使用上面的函数,但是这次将之放入函数文件functions.sh里。sh意即shell脚本
#!/bin/sh # functions.sh # main scripts functions is_it_a_directory() { # is_it_a_directory # to call: is_it_a_directory directory_name _DIRECTORY_NAME=$1 if [ $# -lt 1 ]; then echo "is_it_a_directory: I need a directory name to check" return 1 fi # is it a directory ? if [ ! -d $_DIRECTORY_NAME ]; then return 1 else return 0 fi } # -------------------------------------------- error_msg() { # error_msg # beeps: display messages; beeps again! echo -e "/007" echo $@ echo -e "/007" return 0 }
现在编写脚本就可以调用functions.sh中的函数了。注意函数文件在脚本中以下述命令格式定位:
. <path to file>
使用这种方法不会创建另一个shell,所有函数均在当前shell下执行。
#!/bin/sh # direc_check # source the funtion file fuctions.sh # that's a <dot><space><forward slash> . ./functions.sh # now we can use the fuctions(s) echo -n "Enter destination directory :" read DIREC if is_it_a_directory $DIREC then : else error_msg "$DIREC does not exist ... creating it now!" mkdir $DIREC > /dev/null 2>&1 if [ $? != 0 ]; then error_msg "Could not create directory:: check it out!" exit 1 else : fi fi # not a directory echo "extracting files ..."
执行结果如下所示:
# sh direc_check.sh Enter destination directory :AUDIT AUDIT does not exist...creating it now extracting files ...
文:马哥Linux团队文章出处:运维部落