Shell編程入門
一.Shell的自訂
shell是使用者和Linux作業系統之間的介面。Linux中有多種shell,其中預設使用的是Bash.
Linux系統的shell作為作業系統的外殼,為使用者提供使用作業系統的介面。它是命令語言、命令解釋程式及程式設計語言的統稱。
shell是一個命令語言解譯器,它擁有自己內建的shell命令集,shell也能被系統中其他應用程式所調用。使用者在提示符下輸入的命令都由shell先解釋然後傳給Linux核心。
shell的另一個重要特性是它自身就是一個解釋型的程式設計語言,shell程式設計語言支援絕大多數在進階語言中能見到的程式元素,如函數、變數、數組和程式控制結構。shell程式設計語言簡單易學,任何在提示符中能鍵入的命令都能放到一個可執行檔shell程式中。
二.Shell編程指令碼流程
注意:在編寫指令碼時:
程式必須以下面的行開始(必須放在檔案的第一行):
#!bin/sh
符號#!用來告訴系統後面的參數是用來執行該檔案的程式。在這個例子中我們使用/bin/sh來執行程式。當編輯好指令碼時, 如果要執行該指令碼,還必須使用可執行。
要使指令碼可執行:編譯chmod +x filename 這樣才能用 ./filename 來運行
三.shell基本語句
shell的基本文法主要就是如何輸入命令運行程式以及如何在程式之間通過shell的一些參數提供便利手段來進行通訊。
具體的三種情況:
重新導向
管道pipe
前台和後台
具體內容請參見(1-1.doc)。
四.Shell的變數
系統變數
使用者變數
環境變數
<一>系統變數
Shell常用的系統變數並不多,但卻十分有用,特別是做一些參數檢測的時候。下面是Shell常用的系統變數 :
$n $1 表示第一個參數,$2 表示第二個參數 ...
$# 命令列參數的個數
$0 當前程式的名稱
$? 前一個命令或函數的返回碼
$* 以“參數1 參數2 ... ” 形式儲存所有參數
$@ 以“參數1” “參數2” ... 形式儲存所有參數
$$ 本程式的(進程ID號)PID
$! 上一個命令的PID
其中使用得比較多得是 $n $# $0 $?
樣本:請參見(1-2.doc)
<二> shell使用者變數
不管系統變數有多少,對於需求來說,總是不夠的。使用者變數是最常用到的變數,使用也十分簡單。
使用者定義的變數必須由字母數字及底線組成,並且變數名的第一個字元不能為數字, 與其它UNIX名字一樣,變數名是大小寫敏感的. 對於使用者變數,使用者可按如下方式賦值:
name=“wanggou” 在引用變數時,需在前面加$符號,使用者也可以在變數間進行相互賦值,如:
name="wanggou"
wanggou=$name
echo "Hello $wanggou !“
輸出結果應該很清楚:Hello wanggou !
注意:這裡需要注意一點:變數和'='之間不要有空格,'='和賦值也不要有空格,否則shell不會認為變數被定義。掌握了基本的使用方法,你可以完全開始你的編程工作了
<三>shell環境變數
shell 環境變數是所有shell 程式都會接受的參數。shell程式運行時,都會接收一組變數,這組變數就是環境變數。常用的環境變數,請參見〈1-3.doc>.
這些變數,要關注的最多的就是PATH.
如何去定義新的環境變數:
定義新的環境變數,我們要用到export,看以下樣本:
export MY_NAME=Wanggou
export PATH=/home/wanggou/bin:$PATH
<四>shell變數進行算術運算
五. shell語句
條件陳述式
Shell程式中的條件陳述式主要有if語句、case語句;
迴圈語句
shell常見的迴圈語句有for迴圈、while迴圈、until迴圈
<一> test 測試命令
與傳統的語言不同的是,shell用於指定條件值的不是布林運算式而是命令和字串。(在學習條件陳述式前,我們對test測試命令有個整體的 認識是必要的。)
test命令用於檢查某個條件是否成立,它可以進行數值、字元和檔案三個方面的測試。
具體內容請參見(1-5.doc).
<二>迴圈語句之for迴圈
for迴圈
格式: for var in arg1 arg2 ... argn
do command
....
command
done
注意:var是變數名,arg1-n是數值列表。do到done之間是命令列表。
樣本:請參見(1-6.doc)
<三>迴圈語句之while迴圈
while和until命令都是用命令的返回狀態值來控制迴圈的
格式為
<四>迴圈語句之Until迴圈
<五>條件陳述式之if
<六>條件陳述式之case
文法:case 字串 in 值1|值2) 操作:: 值3|值4) 操作:: 值5|值6) 操作:: *} 操作:: esac case的作用就是當字串與某個值相同是就執行那個值後面的操作。如果同一個操作對於多個值,則使用”|”將各個值分開。在case的每一個操作的最後面都有兩個”::”,分號是必須的。
樣本:請參見(1-10.doc).
<七>無條件控制語句
break用於立即終止當前迴圈的執行,而contiune用於不執行迴圈中後面的語句而立即開始下一個迴圈的執行。這兩個語句只有放在do和done之間才有效。
樣本:請參見(1-11.doc)。
六.函數的定義
shell函數是一種為後續操作組織命令的方法,使用單個名字來命名這些命令組或者 routine。在shell或者是指令碼當中,這個過程的名字必須是唯一的。所有用來組織函數的命令就像普通命令一樣執行。當以一個簡單的命令名來調用函數的時候,和該函數相關的命令就被執行。函數在必須聲明,然後在shell裡執行:沒有新的進程會被建立來打斷這個命令。
注意:特殊的內建命令在命令尋找中先於shell函數。特殊的內建命令:break, :, ., continue, eval, exec, exit, export, readonly, return, set, shift, trap 和 unset.
七.函數的形式
函數使用以下2種形式。
function FUNCTION { COMMANDS; }
或者
FUNCTION () { COMMANDS; }
兩者都定義了一個shell函數 FUNCTION
注意:內建的 function 命令是可選的;然而,如果不使用,必須在函數名後加上小括弧。
八.函數中的位置參數
Shell函數:他們可以接受參數,他們可以使用只有在函數中有效變數(使用本地內建 local )並且他們可以向調用者返回參數。
當函數開始執行,在執行期間函數的參數變成位置參數。擴充為位置參數的數量的特別參數 # 更新來反映這個變化。位置參數 0 未改變。Bash變數 FUNCNAME 在函數執行時被設定為函數的名字。
如果內建的 return 在函數中執行時,在調用完成後函數完成並且執行下一條命令。當函數完成後,位置參數的值和特別參數 # 被重設到函數執行前的值。如果一個數字參數被賦予 return ,那個狀態就返回了。樣本:請參見(1-13.doc)。
九.顯示函數
所有被當前shell所識別的函數可以用 set 顯示出來。函數在他們被使用後保留,除非使用後進行 unset 。
九.函數在指令碼中的應用
<一>.迴圈利用
在你的系統中有大量的指令碼使用函數來以結構化的方法處理一系列命令。在某些linux系統上,比如,你可以找到 /etc/rc.d/init.d/functions 定義檔案,source在所有的初始化指令碼中。使用這個方法通常只需要編寫一次,常見的任務比如檢查進程是否運行,開始或者停止一個守護進程等等。如果某些任務還需要一次,代碼就可以重新迴圈使用。
樣本:參見(1-14.doc)。
<二>.設定路徑
<三>. 遠程備分
參見執行個體如下
1-1.doc
(1)輸入輸出重新導向
在shell中,使用者可以利用“>”和“<”來進行輸入輸出重新導向。如:
command>file:將命令的輸出結果重新導向到一個檔案。
command>&file:將命令的標準錯誤輸出一起重新導向到一個檔案。
command>>file:將標準輸出的結果追加到檔案中。
command>>&file:將標準輸出和標準錯誤輸出的結果都追加到檔案中。
command。< p="" />
(2)管道pipe
pipe同樣可以在標準輸入輸出和標準錯誤輸出間做代替工作,這樣一來,可以將某一個程式的輸出送到另一個程式的輸入,其文法如下:
command1| command2[| command3...]
也可以連同標準錯誤輸出一起送入管道:
command1| &command2[|& command3...]
(3)前台和後台
在shell下面,一個新產生的進程可以通過用命令後面的符號“;”和“&”來分別以前台和背景方式來執行,文法如下:
command
產生一個前台的進程,下一個命令須等該命令運行結束後才能輸入。
command &
產生一個背景進程,此進程在後台啟動並執行同時,可以輸入其他的命令。
1-2.doc
#!/bin/bash
#This file is used to explain the shell system variable.
echo "the number of parameter is $# ";
echo "the return code of last command is $?";
echo "the script name is $0 ";
echo "the parameters are $* ";
echo "/$1 = $1 ; /$2 = $2 ";
1-4.doc
例如:
$expr 2 + 1
結果顯示:3
$expr 5 - 3
結果顯示:2若expr的一個參數是變數,那麼在運算式計算之前用變數值替換變數名。
$int=3
$expr $int + 4
結果顯示:7
使用者不能單純使用"*"做乘法,若輸入:
$expr 4*5
系統將會報錯,因為Shell看到"*"將會首先進行檔案名稱替換。正確形式為:
$expr 4 /* 5
結果顯示:20
多個算術運算式可以組合在一起,例如:
$expr 5 + 7 / 3
結果顯示:7
運算次序是先乘除後加減,若要改變運算次序,必須使用"`"號,如:
$int=`expr 5 + 7`
$expr $int/3
結果顯示:4
或者:
$expr `expr 5+7` / 3
結果顯示:4
注意:紅色的“$”是普通使用者的意思。
1-5.doc
(1)數值測試:
-eq:等於則為真
-ne:不等於則為真
-gt:大於則為真
-ge:大於等於則為真
-lt:小於則為真
-le:小於等於則為真
(2)字串測試:
=:等於則為真
!=:不相等則為真
-z字串:字串長度偽則為真
-n字串:字串長度不偽則為真
(3)檔案測試:
-e檔案名稱:如果檔案存在則為真
-r檔案名稱:如果檔案存在且可讀則為真
-w檔案名稱:如果檔案存在且可寫則為真
-x檔案名稱:如果檔案存在且可執行則為真
-s檔案名稱:如果檔案存在且至少有一個字元則為真
-d檔案名稱:如果檔案存在且為目錄則為真
-f檔案名稱:如果檔案存在且為普通檔案則為真
-c檔案名稱:如果檔案存在且為字元型特殊檔案則為真
-b檔案名稱:如果檔案存在且為塊特殊檔案則為真
注意:另外,Linux還提供了與(“!”)、或(“-o)、非(“-a”)三個邏輯操作符用於將測試條件串連起來,其優先順序為:“!”最高,“-a”次之,“-o”最低。
1-6.doc
#!/bin/bash
for wangluo in wanggou wangguan
do
echo $wangluo
done
1-7.doc
樣本:用“while”計算1到5的平方
#!/bin/bash
int=1
while [ $int -le 5 ]
do
sq=`expr $int //* $int`
echo $sq
int=`expr $int + 1`
done
echo "恭喜成功,接著來看寫一個。"
1-8.doc
樣本:使用until結構計算1-5的平方
#!/bin/bash
int=1
until [ $int -gt 5 ]
do
sq=`expr $int //* $int`
echo $sq
int=`expr $int + 1`
done
echo "成功了,恭喜你!!繼續努力呀。"
1-9.doc
#!/bin/bash
if [ "$SHELL" = "/bin/sh" ]; then
echo "your login shell is the sh "
else
echo "your login shell is not bash but $SHELL"
fi
1-10.doc
#!/bin/bash
case $USER in
beichen)
Echo “You are beichen!”;;
liangnian)
echo “You are liangnian”; //注意這裡只有一個分號
echo “Welcome!”;; //這裡才是兩個分號
root)
echo “You are root!:echo Welcome!”;; //將兩命令寫在一行,用一個分號作為分隔字元
*)
echo “Who are you?$USER?”;;
esac
輸出的結果是:
You are root!
Welcome!
1-11.doc
#!/bin/bash
echo "What is your favourite OS?"
select var in "Linux" "Gnu Hurd" "Free BSD" "Other"; do
break
done
echo "You have selected $var"
注意:break要放在do和done之間才有效.
1-12.doc
#!/bin/bash
function func() {
echo $1
echo $2
return 1
}
func "Hello" "What is your name?"
1-13.doc
#vi jiaoben
#!/bin/bash echo "This script demonstrates function arguments."
echo
echo "Positional parameter 1 for the script is $1."
echo
test ()
{
echo "Positional parameter 1 in the function is $1."
RETURN_VALUE=$?
echo "The exit code of this function is $RETURN_VALUE."
} test other_param
退出儲存
chmod a+x jiaoben #賦予可執行許可權
./jiaoben understand? #執行jiaoben
1-14.doc
# Check if $pid (could be plural) are running
checkpid() {
local i
for i in $* ; do
[ -d "/proc/$i" ] && return 0
done
return 1
}
注意:這個函數在相同的指令碼中在重用於其他指令碼的其他函數中被重用。 守護進程 ,比如,多數使用在啟動一個服務進程的起始指令碼中
1-15.doc
pathmunge () {
if ! echo $PATH | /bin/egrep -q "(^|:)$1($|:)" ; then
if [ "$2" = "after" ] ; then
PATH=$PATH:$1
else
PATH=$1:$PATH
fi
fi
}
# Path manipulation
if [ `id -u` = 0 ]; then
pathmunge /sbin
pathmunge /usr/sbin
pathmunge /usr/local/sbin
fi
pathmunge /usr/X11R6/bin after
unset pathmunge
注釋:這個函數把第一個參數設定為路徑名。如果路徑名不在當前路徑中,就把它加入。傳給函數的第二個參數定義了路徑是加入到當前 PATH 之前還是之後。
普通使用者只把 /usr/X11R6/bin 加入到他們的路徑中,當 root 得到了一些包含系統命令的額外目錄。使用完畢後,函數就進行unset所以就不存在了。
1-16.doc
在星期天,只有 bupbash 運行。
#/bin/bash
LOGFILE="/nethome/tille/log/backupscript.log"
echo "Starting backups for `date`" >> "$LOGFILE"
buplinux()
{
DIR="/nethome/tille/xml/db/linux-basics/"
TAR="Linux.tar"
BZIP="$TAR.bz2"
SERVER="rincewind"
RDIR="/var/www/intra/tille/html/training/"
cd "$DIR"
tar cf "$TAR" src/*.xml src/images/*.png src/images/*.eps
echo "Compressing $TAR..." >> "$LOGFILE"
bzip2 "$TAR"
echo "...done." >> "$LOGFILE"
echo "Copying to $SERVER..." >> "$LOGFILE"
scp "$BZIP" "$SERVER:$RDIR" > /dev/null 2>&1
echo "...done." >> "$LOGFILE"
echo -e "Done backing up Linux course:/nSource files, PNG and EPS images./nRubbish removed." >> "$LOGFILE"
rm "$BZIP"
}
bupbash()
{
DIR="/nethome/tille/xml/db/"
TAR="Bash.tar"
BZIP="$TAR.bz2"
FILES="bash-programming/"
SERVER="rincewind"
RDIR="/var/www/intra/tille/html/training/"
cd "$DIR"
tar cf "$TAR" "$FILES"
echo "Compressing $TAR..." >> "$LOGFILE"
bzip2 "$TAR"
echo "...done." >> "$LOGFILE"
echo "Copying to $SERVER..." >> "$LOGFILE"
scp "$BZIP" "$SERVER:$RDIR" > /dev/null 2>&1
echo "...done." >> "$LOGFILE"
echo -e "Done backing up Bash course:/n$FILES/nRubbish removed." >> "$LOGFILE"
rm "$BZIP"
}
DAY=`date +%w`
if [ "$DAY" -lt "2" ]; then
echo "It is `date +%A`, only backing up Bash course." >> "$LOGFILE"
bupbash
else
buplinux
bupbash
fi
echo -e "Remote backup `date` SUCCESS/n----------" >> "$LOGFILE"