9个函数示例,让你重新认识Shell

一、测试目录存在

拷贝文件时,测试目录是否存在是常见的工作之一。以下函数测试传递给函数的文件名是否是一个目录。因为此函数返回时带有成功或失败取值,可用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

二、提示Y或N

许多脚本在继续处理前会发出提示。大约可以提示以下动作:

• 创建一个目录。
• 是否删除文件。
• 是否后台运行。
• 确认保存记录。
等等

以下函数是一个真正的提示函数,提供了显示信息及缺省回答方式。缺省回答即用户按下回车键时采取的动作。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号中抽取信息

当所在系统很庞大,要和一登录用户通信时,如果忘了用户的全名,这是很讨厌的事。比如有时你看到用户锁住了一个进程,但是它们的用户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团队
文章出处:运维部落

转载请注明:运维派 » 9个函数示例,让你重新认识Shell

3
5.0k
0