Linux之19——Shell程式設計基礎詳解

會跑的熊 發表於 2021-07-18
Linux

第一部分:Linux Shell 簡介

Shell 是一個用 C 語言編寫的程式,它是使用者使用 Linux 的橋樑。Shell 既是一種命令語言,又是一種程式設計語言。
Shell 是指一種應用程式,這個應用程式提供了一個介面,使用者通過這個介面訪問作業系統核心的服務。
Ken Thompson 的 sh 是第一種 Unix Shell,Windows Explorer 是一個典型的圖形介面 Shell。
當一個使用者登陸linux 系統後,系統就會為該使用者建立一個shell程式。
Shell的版本:

  • Bourne Shell:是貝爾實驗室開發的,unix普遍使用的shell,在程式設計方面比較優秀,但在使用者互動方面沒有其他shell優秀。
  • BASH:是GNU的Bourne Again Shell,是GNU作業系統上預設的shell,在bourne shell基礎上增強了很多特性,如命令補全,命令歷史表等等
  • Korn Shell:是對Bourne Shell 的發展,在大部分內容上與Bourne Shell相容,整合了C Shell和Bourne shell優點。
  • C Shell:是SUN公司Shell的BSD版本,語法與c語言相似,比bourne shell 更適合程式設計

第二部分 shell 程式設計基礎

2.1 shell輸入輸出

2.1.1 echo

echo命令可以顯示文字行或變數取值,或者把字串輸入到檔案中
格式: echo string
echo的常用功能:\c 不換行 \f 不進紙 \t 跳格 \n 換行

note:
對於linux系統,必須使用-e選項來使以上轉義符生效

例:

$ echo  -e  "hello\tboy"
hello	boy

echo命令對特殊字元敏感,如果要輸出特殊字元,需要用\遮蔽其特殊含義。
常用的特殊字元:雙引號"" 反引號`` 反斜線\
例:

$ echo "\"\""      //想輸出""
""

2.1.2 read

read命令從鍵盤或者檔案的某一行文字中讀入資訊,並將其賦給一個變數。
如果只指定了一個變數,read會把所有的輸入賦給該變數,直至遇到第一個檔案結束符或回車
格式: read var1 var2 …
例1:

chenshifengdeMacBook-Pro:~ chenshifeng$ read name
Hello I am superman
chenshifengdeMacBook-Pro:~ chenshifeng$ echo $name
Hello I am superman

如果輸入的值個數多於變數個數,多餘的值會賦給最後一個變數:
例2:

chenshifengdeMacBook-Pro:~ chenshifeng$ read name surname
John Mike Kate
chenshifengdeMacBook-Pro:~ chenshifeng$ echo $surname   
Mike Kate
chenshifengdeMacBook-Pro:~ chenshifeng$ 

2.1.3 cat

cat可以用來顯示檔案,並且支援將多個檔案串連線後輸出

note:該命令一次顯示完整個檔案,若想分頁檢視,需使用more

格式: cat [ options ] filename1 … filename2 …
常用options:

  • -v 顯示控制字元
  • -n 對所有輸出行進行編號
  • -b 與-n相似,但空白行不編號

例:

$ cat  file1 file2 file3       // 同時顯示三個檔案
$ cat –b file1 file2 file3

2.1.4 管道 |

可以通過管道把一個命令的輸出傳遞給另外一個命令做為輸入
格式: 命令1 | 命令2
例:

$ cat test.txt | grep 'hello'

2.1.5 tee

把輸出的一個副本輸送到標準輸出,另一個副本拷貝到相應的檔案中
如果想看到輸出的同時,把輸出也同時拷入一個檔案,這個命令很合適
格式: tee -a file

  • -a 表示檔案追加到末尾
  • file 表示儲存輸出資訊的檔案

tee命令一般和管道符|結合起來使用
例:

$ who | tee who.info      // 該命令的資訊返回在螢幕上,同時儲存在檔案who.info中
$ who | tee who.info
chenshifeng console  Jan  9 12:56 
chenshifeng ttys000  Jan  9 13:27 
chenshifeng ttys004  Jan  9 19:11 
chenshifeng ttys005  Jan 10 00:12 
$ cat who.info 
chenshifeng console  Jan  9 12:56 
chenshifeng ttys000  Jan  9 13:27 
chenshifeng ttys004  Jan  9 19:11 
chenshifeng ttys005  Jan 10 00:12 

2.1.6 標準輸入,輸出和錯誤

當我們在shell中執行命令的時候,每個程式都和三個開啟的檔案相聯絡,並使用檔案描述符來引用這些檔案,見下表

檔案 檔案描述符
輸入檔案-標準輸入 0
輸出檔案-標準輸出 1
錯誤輸出檔案-標準錯誤 2

系統中實際上有12個描述符,可以任意使用檔案描述符3-9
標準輸入 對應檔案描述符0,是命令的輸入,預設鍵盤
標準輸出 對應檔案描述符1,是命令的輸出,預設螢幕或檔案
標準錯誤 對應檔案描述符2,是命令錯誤的輸出,預設螢幕或檔案
利用檔案重定向功能對命令的標準輸入,輸出和錯誤進行修改。
常用檔案重定向命令:

command > file:                   標準輸出重定向到一個檔案,錯誤仍然輸出螢幕
command >> file:                  標準輸出重定向到一個檔案(追加)
command 1> file:                  標準輸出重定向到一個檔案
command 2>> file:                 標準錯誤重定向到一個檔案(追加) 
command >file 2>&1:               標準輸出和標準錯誤一起重定向到一個檔案
command >>file 2>&1:              標準輸出和標準錯誤一起重定向到一個檔案(追加)
command < file1 >file2:           以file1做為標準輸入,file2做為標準輸出
command <file:                    以file做為檔案標準輸入  

結合使用標準輸出和標準錯誤

$ cat hello 1>myfile.out  2>myerror.out 

合併標準輸出和標準錯誤

$ cat >>mylog.out  2>&1  <hello 

2.2 shell後臺執行命令

2.21 cron

cron是系統的排程程式,可在無人干預的情況下執行作業,通過crontab的命令允許使用者提交,編輯或者刪除相應的作業。
每個使用者都可以有一個crontab檔案來儲存排程資訊,通過該命令執行任意一個shell指令碼或者命令
在大的系統中,系統管理員可以通過/etc/cron.allow和/etc/cron.deny這兩個檔案來禁止或允許使用者擁有自己的crontab檔案

crontab的域
第1列 分鐘0~59
第2列 小時0~23(0表示子夜)
第3列 日1~31
第4列 月1~12
第5列 星期0~6(0表示星期天)
第6列 要執行的命令

crontab格式: 分<>時<>日<>月<>星期<>要執行的命令
<>表示空格

note:如果要表示範圍的話,如週一到週五,可以用1-5表示
如果要列舉某些值,如週一、週五,可以用1,5表示

例1:

30  21  *  *  *  /apps/bin/cleanup.sh

例2:

0,30  18-23  *  *  *  /apps/bin/dbcheck.sh

crontab的命令選項
格式:crontab [ -u user ] -e -l -r
其中

  • -u 使用者名稱,如果使用自己的名字登陸,就不用使用-u選項
  • -e 編輯crontab檔案
  • -l 列出crontab檔案中的內容
  • -r 刪除crontab檔案

建立一個新的crontab檔案

1 建立一個檔案,建議名為cron,例shifengcron,在檔案中假如如下內容:

0,10,20,30,40,50  *  *  *  *  /bin/echo "hello boy"

儲存退出
2 提交剛剛建立的cron檔案shifengcron

$ crontab shifengcron
$ ls /var/spool/cron/ 是否生成檔案shifengcron

列出crontab檔案

$ crontab –l
$ crontab –l  >  $HOME/mycron   可以通過這種方法對crontab進行備份

編輯crontab檔案

$ crontab  -e 

修改後儲存退出,cron會對其進行必要的完整性檢查

刪除crontab檔案

$ crontab –r

crontab檔案的恢復

如果誤刪了crontab檔案,假設在$HOME目錄下還有備份,可以將這個備份檔案拷貝到/var/spool/cron/ username是使用者名稱,如果由於許可權問題無法拷貝,可以使用

$ crontab <filename>   

note:filename是備份的crontab檔案的名字

2.22 at

at命令允許使用者向cron守護程式提交作業,使其在稍後的時間執行,這個時間可以是10min以後,也可能是幾天以後,但如果時間比較長,建議還是使用crontab
格式:at [ -f script ] [ -m -l -r ] [ time ] [ date ]

  • -f script 是要提交的指令碼或命令
  • -m 作業完成後給使用者發郵件
  • -r 清除某個作業,需要提供作業標識id
  • time 作業執行的時間格式可以為:HH. MM ,HH:MM
  • H代表小時,M代表分鐘
  • date 日期格式可以是月份數或日期數,而且at命令可以識別諸如today,tomorrow這樣的詞

可以通過命令列方式或者at命令提示符方式來提交作業,一般來講,如果提交多個命令,可以使用at命令提示符;如果提交的是shell指令碼,可以使用命令列方式
例:提示符方式:

$ at 01:15
at > echo “hello”
at > echo “boy”  >/home/wuxh/at.log
at > <EOT>

note:EOT是Ctrl+D,任務執行後,會給當前使用者傳送郵件,通過mail命令可以檢視相關資訊,也可以將資訊重定向到檔案

例:提交shell指令碼方式

$ at  3:00pm  tomorrow –f   /home/wuxh/hello.sh

note:該指令碼將在明天下午3點執行,使用指令碼方式,要加引數-f

列出at任務,格式:at -l
例:

$ at  -l  
5	2021-01-17	11:20	a	root

note: 第一個是作業標識id;第二個是日期;第三個是時間;a代表at;第四個代表建立任務的使用者
清除at任務
格式:at -r

$ at  –r  [ job no]  

例:$ at -r 5

note:不接job no將清除所有未執行的任務,接具體job id將清楚對應的任務

2.23 &

當在前臺執行某個作業時,終端被該作業佔據;而當它在後臺執行時,它不會佔據終端
可以藉助&命令把作業放到後臺執行
格式: 命令 &

注:
1 .需要使用者互動的命令不要放在後臺執行,否則機器一直等待
2 .後臺程式在執行時,執行結果仍然會輸出到螢幕,干擾我們的工作,建議將這樣的資訊重定向到某個檔案
即:command > out.file 2>&1 &
將標準輸入錯誤輸出都定向到一個out.file的檔案中
例:$ find /etc/ -name "hello" -print >find.dt 2>&1 &

2.3 引號

"" 雙引號
` 反引號
'' 單引號
\ 反斜線

2.31 雙引號

可引用除字元$,`,\外的任意字元或者字串,對$,`,\敏感

例1:

$ echo "hello"
hello

例2:

$ echo "$$"
8311            ///想輸出字元$$  結果看到的是數值8311 
$ echo "\$$"       //對特殊字元需要反斜線遮蔽其特殊含義
$$               //得到想要的結果

例3:

$ echo "`V_V`"  //想輸出`V_V`字樣    結果得到錯誤資訊
$ echo "\`V_V\`"    //得到`V_V`輸出

2.32 單引號

單引號和雙引號的用法基本類似,不同的是單引號對特殊字元不敏感,可以將其做為普通字元輸出出來
例:

$ echo '$$'               //結果 $$  不用藉助\進行遮蔽
$ echo '`V_V`'          //結果`V_V`,和前面雙引號比較

2.33 反引號

該命令用於設定系統命令的輸出到變數,shell將反引號中的內容做為命令執行。
例1:

$ echo `hello`
-bash: hello: command not found

例2:

$ echo `date`
2021年 1月17日 星期日 23時40分18秒 CST

反引號可以和雙引號結合起來使用:
例3:

$ echo "The date today is `date`"
The date today is 2021年 1月17日 星期日 23時41分15秒 CST

2.34 反斜線

如果一個字元有特殊含義,為防止shell誤解其含義,可用\遮蔽該字元
具有特殊含義的字元
------------------------------------------------------------------------------------
& * ^ $ ` “ |
------------------------------------------------------------------------------------
例1 :

$ echo "$$"           //在螢幕上輸出$$字元,結果顯示3853
$ echo "\$$"          //用反斜線遮蔽,防止shell誤解,結果顯示$$

例2:

$ echo  *         //在螢幕上輸出*字元,結果輸出當前目錄下內容
$ echo  \*            //用反斜線遮蔽,防止shell誤解,輸出*字元

2.4 shell變數,引數

2.4.1 系統變數

系統變數適用於所有使用者程式,可以在命令列中設定,但使用者登出時這些值將丟失,最好在.bash_profile中進行定義,或者/etc/profile
傳統上,所有環境變數都大寫,且必須用export命令匯出
設定環境變數:

var_name=value; export var_name

或者:

var_name=value
export var_name

又或者

export var_name=value

檢視環境變數:

echo $var_name
  • env 該命令可檢視所有系統環境變數
  • unset var_name 清除系統環境變數

嵌入shell變數
一般來講,bourne shell有一些預留的環境變數名,這些變數名不能做其他用途,通常在/etc/profile中建立這些嵌入的環境變數,但這不絕對,取決於使用者
shell的變數列表:
CDPATH; EXINIT; HOME; IFS; LOGNAME; MAIL;MAILCHECK; PATH; PS1; PS2; SHELL; TERMINFO;TERM; TZ

2.4.2 使用者變數

在使用者shell生命週期的指令碼中使用,不同的使用者可以定義各自的使用者變數 ~/.bashrc
用法:

var_name=value

顯示變數:

echo $var_name
or  echo ${var_name}   //建議使用

清除變數:

unset var_name

顯示使用者所有變數:set
測試變數是否設定:echo ${var:=value} 若未設定或未初始化,可用新值

使用變數儲存系統命令引數
例:

$ SOURCE="/etc/passwd"
$ DEST="/home/chenshifeng/
$ cp $SOURCE $DEST	

設定只讀變數
可設定某個變數為只讀方式,只讀變數不可更改,否則系統返回錯誤
用法:

var_name=value
readonly var_name   

例:

$ myvar="100"
$ readonly myvar
$ myvar="200"        
$ -bash: myvar: readonly variable

2.4.3 位置變數

位置變數屬於只讀變數
作用:向shell指令碼傳遞引數,引數個數可以任意多,但只有前9個被訪問到,shift命令可以更改這個限制。
每個訪問引數前加$,
第一個引數為0,表示預留儲存實際指令碼名字,無論指令碼是否有引數,此值均可用,如:給指令碼test傳遞資訊:
Would you like to do it

$0 $1 $2 $3 $4 $5 $6 $7 $8 $9
指令碼名字 would you like to do it

例:$ vi test

#!/bin/sh
echo "The full name is : $0 "
echo "The script name is : `basename $0`"
echo "The first parameter is :$1"
echo "The second parameter is :$2"
echo "The third parameter is :$3"
echo "The fourth parameter is :$4"
echo "The fifth parameter is :$5"
echo "The sixth parameter is :$6"
echo "The seventh parameter is :$7"
echo "The eighth parameter is :$8"
echo "The ninth parameter is :$9"

儲存檔案,執行 $ ./test would you like to do it

The full name is : ./test 
The script name is : test
The first parameter is :would
The second parameter is :you
The third parameter is :like
The fourth parameter is :to
The fifth parameter is :do
The sixth parameter is :it
The seventh parameter is :
The eighth parameter is :
The ninth parameter is :

note:上例中$0返回的資訊中包含路徑名,如果只想得到指令碼名稱,可以藉助basename,將指令碼中第一句修改為:
echo "The script name is : \`basename \$0\` "
儲存檔案,執行 ./test would you like to do it

note:basename 用``向系統命令傳遞引數

可以在指令碼中向系統命令傳遞引數

$ vi findfile
#!/bin/sh
find / -name  $1

儲存,執行

$ ./findfile  passwd

2.4.4 特定變數

特定變數屬於只讀變數,反映指令碼執行過程中的控制資訊
特定的shell變數列表:

變數 說明
$# 傳遞到指令碼的引數個數(常用)
$* 以一個單字串的形式顯示所有向指令碼傳遞的引數,與位置變數 不同,此項引數可超過9個
$$ 指令碼執行的當前程式id號(常用)
$! 後臺執行的最後一個程式的程式id號
[email protected] 與$*相同,但是使用時加引號,並在引號中返回每個引數
$- 顯示shell使用的當前變數,與set命令功能相同
$? 顯示最後命令的退出狀態,0表示正確,其他任何值表示錯誤(常用)

例:修改test指令碼,追加特定變數資訊:

#!/bin/sh
echo "The full name is : $0 "
echo "The script name is : `basename $0`"
echo "The first parameter is :$1"
echo "The second parameter is :$2"
echo "The third parameter is :$3"
echo "The fourth parameter is :$4"
echo "The fifth parameter is :$5"
echo "The sixth parameter is :$6"
echo "The seventh parameter is :$7"
echo "The eighth parameter is :$8"
echo "The ninth parameter is :$9"

echo "The number of arguments passed 😒#"
echo "Show all arguments 😒*"
echo "Show my process id :$$"
echo "Show me the arguments in quotes 😒@"
echo "Did my script go with any errors 😒?"

最後的退出狀態 $?
可以在任何指令碼或者命令中返回此變數以獲得返回資訊,基於此資訊,可以在指令碼中做更進一步的研究,返回0為成功,1為失敗
例1:

$ cp /etc/passwd /home/chenshifeng/myfile
$ echo $?
0

例2:

$ cp /etc/passwd /home/wuxh/mydir   //<mydir不存在>
$ echo $?
1

建議將返回值設定為一個有意義的名字,增加指令碼的可讀性
修改例2

$ cp_status=$?
$ echo $cp_status

第三部分 shell程式設計流程控制

3.1 test 測試命令

3.1.1 檔案測試

測試檔案狀態:
用法:test condition 或者 [ condition ]
檔案狀態列表

  • -d 目錄
  • -s 檔案長度大於0,非空
  • -f 正規檔案
  • -w 檔案可寫
  • -L 符號檔案
  • -u 檔案有uid設定
  • -r 檔案可讀
  • -x 檔案可執行

例:

$ ls  -l  hello
$ [  -w hello  ]    
$ echo $?

使用邏輯操作符:
測試檔案狀態是否ok,可以藉助邏輯操作符對多個檔案狀態進行比較

  • -a 邏輯與,操作符兩邊均為真,結果為真,否則為假
  • -o 邏輯或,操作符兩邊一邊為真,結果為真,否則為假
  • ! 邏輯否,條件為假,結果為真

例1:

$ [  -r myfile1    -a   -w  myfile2   ] 
$ echo $?

例2:

$ [  -w myfile1    -o   -x  myfile2   ] 
$ echo $?

3.1.2 字串測試

字串測試是錯誤捕獲很重要的一部分,特別是使用者輸入或比較變數時尤為重要
格式:

  • test "string"
  • test string_operator "string"
  • test "string" string_operator "string"
  • [ string_operator string ]
  • [ string string_operator string ]

注:string_operator 的取值:
= 等於 、 != 不等於 、 -z 空串 、 -n 非空串

例:測試變數string1是否等於string2

$ string1="hello"
$ string2="Hello"
$ [  "$string1" = "$string2"  ]
$ echo $?

note:在進行字串比較時,一定要加引號;等號前後要加空格。

3.1.3 數值測試

格式:"number" number_operator "number"
或者:[ "number" number_operator "number" ]
number_operator 的取值範圍:

比較符 說明
-eq 數值相等
-gt 第一個數大於第二個數
-ne 數值不相等
-lt 第一個數小於第二個數
-le 第一個數小於等於第二個數
-ge 第一個數大於等於第二個數

例1:

[[email protected] ~]# NUM1=130
[[email protected] ~]# [ "$NUM1" -eq  "130" ]
[[email protected] ~]# echo $?
0

例2:

[[email protected] ~]# [ "990" -le "996" -a "123" -gt "33" ]
[[email protected] ~]# echo $?
0

3.2 expr 語句-字串測試和數值測試

一般用於整數值,也可以用於字串;
格式:expr argument operator argument
expr 也是個手工命令列的計數器

$ expr  10 + 10                    #注意空格
$ expr 300 /  6  /  5   
$ expr 30  \*  3                   #注意:乘號必須用反斜線遮蔽其特定含義

expr在迴圈中用於增量計算,迴圈初始化為0,然後迴圈加1,常用的做法:從expr接受輸出賦給迴圈變數
例:

$ LOOP=0
$ LOOP=`expr $LOOP + 1`

3.2.1 數值測試

可以用expr測試一個數,如果對非整數進行計算,則返回錯誤
例:

$ expr  1.1 + 1          #返回錯誤
$ expr  1 + 1            #返回2

3.2.2 字串測試

注 expr 也有返回的狀態,但與系統最後返回的值剛好相反,expr返回成功為1,其他值為失敗。
例:

$ value=hello
$ expr $value =  "hello"
1                             # 這是expr執行成功的值
$ echo $?
0                             # 這是系統返回的成功的值

3.3 if 條件判斷

格式:

if 條件1 
    then   命令1
elif  條件2
    then  命令2
else  命令3                 
fi

注意:使用if語句時,必須將then部分放在新行,否則會產生錯誤,如果要不分行,必須使用命令分割符,即:

if  條件1; then
    命令1
fi

例:$ vi myfile

#!/bin/sh
DIRECTORY=$1
if [ "`ls -A $DIRECTORY`" = "" ] ;  then
   echo  "$DIRECTORY  is  indeed empty"
else
   echo  "$DIRECTORY  is  not  empty"
fi 

3.4 for 迴圈

格式:

for 變數名 in 列表
do
    命令1
    命令2
done

說明:命令 可為任何有效的shell命令和語句
變數名可以為任何單詞
in列表是可選的,如果沒有列表,for迴圈會使用命令列的位置引數
in列表可以包含替換,字串,檔名

例:

#!/bin/sh
for loop1 in 1 2 4 5 6           #數字列表
do 
   echo $loop1
done
for loop2  in he is a tall man   #字串列表
do 
   echo $loop2
done
for loop3 in `ls`                #替換列表
do 
  echo $loop3
done

對for 迴圈使用引數,當迴圈中省去in列表選項時,它將接受命令列特定變數做為引數,即

for params in "[email protected]"   或者 for params in "$*"

例1:

#!/bin/sh
for  params in "[email protected]"
do
echo  "You supplied $params as a command line option"
done
echo  $params

例2

#!/bin/sh
counter=0
for files in `ls`
do
    counter=`expr $counter  +  1`
done
echo  "There are $counter  files in `pwd`"

3.5 while和until迴圈

while迴圈
格式:

while  命令
do    
      命令1
      命令2
      ……
done

note:do和done之間命令,只有前一個返回狀態為0,後面命令才會被執行;否則則迴圈中止

until迴圈
格式:

until  條件
do
       命令1
       命令2
       ……
done

note:until執行一系列命令,只至條件為真時停止,迴圈至少執行一次。

例1:

#!/bin/sh
echo "Type  <Ctrl-D> to terminate"
echo  -n  "enter your favorate film :"
while read  FILM
do 
   echo "Yeah,great film the $FILM"
done

使用ctrl-D中斷指令碼的執行,整個迴圈中止
例2:

#!/bin/sh
IS_ROOT=`who | grep root`
until [ "$IS_ROOT" ]
do
    sleep 5
    IS_ROOT=`who | grep root`
done
echo "Watch it. Roots in " | mail chenshifeng

思考:為什麼用sleep 5?

3.6 case 條件選擇

格式:

case  值  in 
      模式1)
            命令1
            ……
            ;;
      模式2)
            命令2
            ……
            ;;
esac

case 取值後面必須為in,每個模式必須以右括號結束,取值可以為變數或者常數,找到匹配模式後,執行相關命令直到;;

模式部分可以包含元字元,與命令列中副檔名中使用的匹配型別相符,如 * 、? 、 [..]
例:

#!/bin/sh
if [  $# != 1 ];  then
      echo "Usage:`basename $0` [start|stop|help]"                  	
      exit 1
fi
OPT=$1
      case $OPT in
      start) 
            echo "starting..`basename $0`"
            # code here to start a process
            ;;
      stop) 
            echo "stopping..`basename $0`"
            # code here to stop a process
            ;;
      help)
            # code here to display a help page
            ;;
      *) 
            echo "Usage:`basename $0` [start|stop|help]"
            ;;
      esac

3.7 break 和continue

有時需要某些準則退出迴圈或者跳過迴圈步,就需要break和continue來實現
break 允許跳出迴圈或者case語句,在巢狀迴圈裡,可以制定跳出的迴圈個數,例在兩層的巢狀迴圈內,break 2可以跳出整個迴圈
continue 類似於break,區別是continue只會跳過當前的迴圈步,而不會跳出整個迴圈
例子1:

#!/bin/sh
while :
do 
echo -n  "Enter any number [1..5]  :"
read ANS
case $ANS  in
      1|2|3|4|5) 
            echo "great you entered a number between 1 and 5"
            ;;
      *) 
            echo "wrong number..bye"
            break
            ;;
esac
done

例子2 :
names2.txt 內容包含僱員名字,部門,及其id,如下所示:
------------------------------內容如下--------------------------------
---LISTING OF PERSONNEL FILE----
--- TAKEN AS AT 06/1999----------------
Louise Conrad:Accounts:ACC8987
Peter James:Payroll:PR489
Fred Terms:Customer:CUS012
James Lenod:Accounts:ACC887
Frank Pavely:Payroll:PR489
-------------------------------------------------------------------------------
要求:讀取names2.txt檔案,將在職員工的名字,部門,部門id讀取列印出來
說明:Peter James已經離職

使用IFS讀檔案
輸出時要去除冒號域分隔符,可使用變數IFS。在改變它之前儲存IFS的當前設定。然後在指令碼執行完後恢復此設定。
使用IFS可以將域分隔符改為冒號而不是空格或tab鍵,這裡有3個域需要加域分隔,即NAME、DEPT和ID。指令碼如下:

#!/bin/sh
# save the setting of IFS
SAVEDIFS=$IFS
# assign new separator to IFS
IFS=:
INPUT_FILE=names2.txt
NAME_HOLD="Peter James"
LINE_NO=0
if [ -s  $INPUT_FILE ]; then
    while  read NAME  DEPT  ID
    do
        LINE_NO=`expr $LINE_NO  +  1`
        if [   "$LINE_NO"  -le  "2"  ];  then
            continue
        fi 
        if  [   "$NAME" = "$NAME_HOLD"  ];  then 
            continue
        else
            echo "Now processing …$NAME $DEPT $ID"
        fi
    done < $INPUT_FILE
    # restore the settings of IFS
    IFS=$SAVEDIFS
else
    echo "`basename $0 `  : Sorry file not found or there is no data in the file >&2"
    exit 1
fi

第四部分 shell 函式

shell 允許將一組命令集或語句形成一個可用塊,這些塊稱為shell函式,其組成部分:
函式標題,函式體
標題是函式名,應該唯一;函式體是命令集合
函式格式:

函式名()
{
   命令1
   …
}
或者  
function 函式名()
               { ….
               }

函式可以只放在同一個檔案中做為一段程式碼,也可以放在只包含函式的單獨檔案中

4.1 在指令碼中定義並使用函式

注:函式必須在使用前定義,一般放於指令碼開始部分,直至shell直譯器首次發現它時,才可以使用

例指令碼func1:

#!/bin/sh
hello() {
echo "Hello,today’s date is `date`"
}
echo "now, going to the function hello"
hello                       
echo "back from the function"

4.2 向函式傳遞引數

向函式傳遞引數就象在一般指令碼中使用特殊變數$1,$2..$9一樣,函式取得所傳引數後,將原始引數傳回shell,可以在函式內定義本地變數儲存所傳的引數,一般這樣的引數名稱以_開頭
例:指令碼對輸入的名字進行檢查,只能包含字母
$ vi func2

#!/bin/sh
echo -n "what is your first name :"
read F_NAME
char_name()
{  
_LETTERS_ONLY=$1
_LETTERS_ONLY=`echo $1|awk  '{if($0~/[^a-z A-Z]/)  print  "1"}'`
if [ "$_LETTERS_ONLY" != "" ]
then       
    return 1
else
    return 0
fi
}
if  char_name  $F_NAME;  then
    echo "ok"
else
    echo "ERRORS"
fi

4.3 函式返回值

函式執行完畢或者基於某個測試語句返回時,可作兩種處理:

  1. 讓函式正常執行到末尾,然後返回指令碼中呼叫函式的控制部分
  2. 使用return 返回指令碼中函式呼叫的下一條語句,可以帶返回值,
  • 0為無錯誤
  • 1為有錯誤
    格式:
return     從函式中返回,用最後狀態命令決定返回值
return 0  無錯誤返回
return 1  有錯誤返回

4.4 函式返回值測試:

可以直接在指令碼呼叫函式語句的後面使用最後狀態命令來測試函式
呼叫的返回值
例:

hello            #這裡是hello函式被呼叫
if [  $? = 0   ]  
then 
      echo "it is ok"
else
      echo "something is wrong with hello function"
fi

更好的辦法是使用if語句測試返回0還是返回1,可以在if語句裡面將函式呼叫用括號括起來,增加可讀性,如 if hello ; then

如果函式將從測試結果中反饋輸出,可以使用替換命令儲存結果,函式呼叫的替換格式為
variable_name=function_name
函式function_name輸出被設定到變數variable_name中

4.5 在shell中使用函式

常用的一些函式可以收集起來,放入函式檔案,使用時,將檔案載入shell中
檔案頭應該以#!/bin/sh開頭,檔名可任意選取,但建議有說明性。
檔案一旦載入shell,就可以在命令列或者指令碼中呼叫函式,可以使用set產看所有定義的函式,輸出列表包括已經載入shell的所有函式。
要想改動檔案,首先用unset命令從shell中刪除函式,注,這裡不是真正的刪除,修改完畢後,再將檔案重新載入,有些shell會識別改動,不必使用unset,但建議改動時使用unset命令

4.6 建立函式檔案

例:function.main

#!/bin/sh
findit() {
if [ $# -lt 1 ];  then
    echo "Usage :findit  file"     # 思考:為什麼用findit,不用$0?
    return  1
fi
find  /  -name  $1  -print
}

4.7 定位檔案(載入檔案)

格式:<點><空格><路徑><檔名>

$.   ./function.main

4.8 檢查載入的函式

使用set命令檢視已經載入shell中的函式

$ set

4.9 執行shell函式

要執行函式,簡單的鍵入函式名,如果需要引數,後跟引數即可

$ findit hello

4.10 修改shell函式

如果需要對函式做改動,需要藉助unset命令先從shell中刪除函式,修改後,再重新載入

$ unset findit
修改…
$ .  ./function.main

第五部分 指令碼除錯——shell指令碼除錯

5.1 一般的shell指令碼錯誤

  • a.迴圈錯誤
    • 如for,while,until,case等結構中漏寫了某個保留字
  • b.漏寫引號
  • c.測試錯誤
    • 如 在-eq 兩邊應該使用數字取值;[變數] 缺少空格
  • d.字元大小寫
    -linux對大小寫敏感

5.2 使用set命令進行除錯

set  -n  讀命令但不執行
set  -v  顯示讀取的所有行
set  -x  顯示所有命令及其引數
set  +x  set選項關閉

例:vi error_file

#!/bin/sh
set –x
LIST="Peter Susan John Barry Lucy Norman Bill Leslie"
echo  -n  "Enter your name :"
read NAME
for LOOP in $LIST
do
if  [ "$LOOP" = "$NAME" ];then
    echo "you’re on the list, you’re in"
    break
fi
done
set +x

執行指令碼

$ ./error_file

執行結果:

error  
+ error
+ LIST=Peter Susan John Barry Lucy Norman Bill Leslie
+ echo –n Enter your Name:
Harry
+ [ Peter = Harry ]
+ [ Susan = Harry  ]
+ [ John = Harry  ]
+ [ Barry = Harry  ]
+ [ Lucy = Harry  ]
+ [ Norman = Harry  ]
+ [ Bill = Harry  ]
+ [ Leslie = Harry  ]