1. 程式人生 > >9個函式示例,讓你重新認識Shell

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 [email protected] | tr '[a-z]' '[A-Z]'
}
str_to_upper $1

變數upper 儲存返回的大寫字串,注意這裡用到特定引數[email protected]來傳遞所有引數。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 [email protected]
        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 [email protected]
        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團隊
文章出處:運維部落