【Shell編程】Shell基本語法
Shell 語法
??Shell程序設計作為一種腳本語言,在Linux系統中有廣泛的應用,本文記錄了關於Shell程序設計的基礎語法知識和常用命令,方便查詢,熟練使用shell也需要經常實踐,這對於完成一些較簡單的編程任務很有幫助。
(1)變量
??在shell裏,使用變量之前並不需要事先做出聲明,可以通過使用直接創建。默認情況下,所有的變量都被當做字符串進行存儲,變量名區分大小寫。變量名之前加一個$符號可以訪問它的內容。
??輸入和輸出:可以使用echo命令將一個變量的內容輸出到終端,使用read命令(停下來等待用戶輸入)可以將用戶的輸入賦值給一個變量。
??單引號和雙引號:shell腳本的文件參數以空白符進行分割,如果一個參數包含空白符,就需要放到引號中(防止被解讀為不同的參數)。雙引號通常可以用來做字符串定界符。而像$salutation這樣的變量放到引號中,有這幾種情況:不加引號引用變量的值,放到雙引號中也引用變量的值,放到單引號中就不引用,而是看作一個字符串。
$ salutation=Hello $ echo $salutation # 輸出 Hello $ salutation="Yes Sir" $ echo $salutation # 輸出 Yes Sir $ salutation=7+2 $ echo $salutation # 輸出 7+2 (作為字符串看待) # 使用引號: $ myvar="Hello World" $ echo $myvar # 輸出 Hello World $ echo "$myvar" # 輸出 Hello World $ echo '$myvar' # 輸出 $myvar $ echo \$myvar # 轉義字符,輸出 $myvar
??除了自定義變量外,shell還有一些環境變量和參數變量。
??所謂環境變量,就是指腳本開始執行時,一些變量會根據當前環境設置中的值進行初始化。使用export命令可以設置環境變量。下面的示例展示了常用的環境變量:
$ echo $HOME # 當前用戶的home目錄,在我的機器上輸出 /home/gzshan
$ echo $PATH # 以冒號分割的用來搜索命令的目錄列表
$ echo $0 # shell腳本的名字
$ echo $# # 傳遞給腳本的參數數量,默認是0
$ echo $$ # shell腳本的進程號
??所謂參數變量,就是指執行腳本程序如果帶有參數,一些額外的變量會被創建,如下所示:
如:運行一個腳本的命令:./first.sh foo bar baz
$1 $2 $3 #分別指的就是參數foo,bar和baz
$# # 此時為3
[email protected] # 用於列出所有的參數變量,($* 也有相同功能,但受空白符影響,一般用[email protected])
(2)條件
??條件判斷是程序設計語言控制結構的基礎,程序需要對條件進行測試和判斷,從而執行不同的命令,完成不同的功能和任務。在shell腳本中,完成條件測試有兩個命令:test 和 布爾判斷命令 [ ]
test 命令
??以下的例子展示了test命令的用法,註意如果then和if放在同一行則需要一個分號。
if test -f fred.c # 如果then寫在這一行需要一個分號,這條語句檢查一個文件是否存在 then echo "Hello" fi
布爾判斷命令[ ]
??把 [ 當作一條命令看起來有些奇怪,但是會使得程序變得簡單。註意:使用 [ 時,後面必須有空格,還應該使用 ] 來結尾。
if [ -f fred.c ] ; then # 同樣必須有分號,必須有空格 echo "test" fi
??當使用以上兩個命令時,可以使用的條件類型歸結為三類,用下表歸納。
第一類:字符串的比較 比較結果 string1 = string2 兩個字符串相同結果為真 string1 != string2 兩個字符串不相同結果為真 -n string 字符串不為空則結果為真 -z string 字符串為null則結果為真 第二類:算術比較 比較結果 expression1 -eq expression2 兩個表達式結果相等為真 expression1 -nq expression2 兩個表達式結果不相等為真 expression1 -gt expression2 expression1 大於 expression2 為真 expression1 -ge expression2 expression1 大於等於 expression2 為真 expression1 -lt expression2 expression1 小於 expression2 為真 expression1 -le expression2 expression1 小於等於 expression2 為真 !expression expression為假則結果為真 第三類:與文件有關的條件測試 比較結果 -d file 文件是一個目錄,則結果為真 -f file 文件是一個普通文件,則結果為真 -g file 文件set-group-id 被設置,則結果為真 -u file 文件set-user-id 被設置,則結果為真 -s file 文件大小不為0,則結果為真 -r / -w / -x file 文件可讀 / 可寫 / 可執行,則結果為真 -a file 文件存在,則結果為為真 -c file 文件存在並且為字符特殊文件,則結果為真
(3)控制結構
if 語句和elif 語句
??if 語句比較簡單,下面的例子展示了if 語句和elif語句的用法。
#!/bin/sh echo "Is it morning? Please answer yes or no" read timeofday if [ $timeofday = "yes" ] # if語句,後面跟條件測試 then echo "Good morning" elif [ $timeofday = "no" ]; then # then放同一行,加分號 echo "Good afternoon" else echo "Sorry,$timeofday not recognized. Enter yes or no" exit 1 fi exit 0
for 語句
??for語句用以循環處理一組值,這組值可以是任意字符串的集合,也可以是其他命令的輸出結果,下面用兩個例子展示其用法。
#!/bin/sh for foo in bar fud 43 # 循環處理一組字符串 do echo $foo done exit 0
#!/bin/sh for file in $(ls f*.sh); do # 使用通配符擴展for循環,列出所有以f開頭,擴展名為.sh的腳本文件 # 說明:$()是執行該命令得到的輸出結果 lpr $file # lpr是打印命令 done exit 0
while 語句
??如果事先不知道循環次數,for循環不太好使用的情況下,可以使用while循環。whie語句的do和done之間的語句反復執行,直到條件不再真為止。
#!/bin/sh echo "Enter password" read trythis while [ $trythis != "secret" ]; do # 反復執行,直到條件不再真為止 echo "sorry,try again" read trythis done exit 0
until 語句
??until語句與while循環類似,所不同的是,until反復執行循環直到條件為真。
#!/bin/sh until who | grep "$1" > /dev/null do sleep 60 done echo -e '\a' echo "$1 has just logged in" exit 0
case 語句
??case語句相比其他結構較為復雜,用下面的例子來介紹他的用法,需要特別註意的是:case按順序查找第一個匹配的模式,而不是最佳匹配。
#!/bin/sh echo "Is it morning? Please answer yes or no" read timeofday case "$timeofday" in yes | y | Yes | YES ) echo "good morning";; # 註意每個模式末尾是兩個分號 n* | N* ) echo "good afternoon";; * ) echo "sorry,answer not recognized";; esac exit 0
命令列表
??有時需要將多個命令連接成一個序列,shell提供了命令列表,也就是and列表和or列表,類似於其他程序設計語言,它們也采用的是短路求值。
if [ -f file1 ] && echo "hello" && [ -f file2 ] && echo "here";then echo "in if" fi if [ -f file ] || echo "hello" || echo "here" ; then echo "in if" fi
語句塊
??在某些只允許使用單個語句的地方,要想使用多條語句,可以放到花括號中構建一個語句塊。
get_confirm && { # and列表中使用語句塊 echo "hello" cat test.txt }
(4)函數
??要在shell腳本中使用函數,只需要寫出函數名,然後跟一對括號,再把函數中的語句放在一對花括號中,並且把函數定義放到函數調用之前。函數體內可以用local關鍵字聲明局部變量。
??函數參數:當一個函數被調用時,腳本程序的位置參數($*,[email protected],$#,$1,$2
等等)會被替換為函數的參數,當函數執行完畢後,這些參數會恢復為先前的值。下面的例子展示了函數的用法。
#!/bin/sh
yes_or_no(){ # 函數定義
echo "Is your name $* ?"
while true
do
echo -n "Enter yes or no: "
read x
case "$x" in
y | yes ) return 0;;
n | no ) return 1;;
* ) echo "Answer yes or no"
esac
done
}
# 以下是主程序部分:
echo "Original parameters are $*"
if yes_or_no "$1"
then
echo "Hi $1,nice name"
else
echo "Never mind"
fi
exit 0
(5)命令
break命令和continue命令
??這兩條命令比較簡單,break應用於跳出for、while、until循環,continue命令用於跳過當前這一次循環。
: 命令
??:相當於一個空命令,或者相當於true的別名,例如 while : 就代表一個死循環
. 命令
??. 命令用於在當前shell中執行命令,如前面用到的
./first.sh
echo 命令和read命令
??echo命令用於輸出結尾帶有換行符的字符串,前面已多次用到,它還有兩個常用的參數,如下所示。現在最新版本的shell常常用printf來代替echo。read命令用於將用戶的輸入賦給一個變量。
$ echo -n "string to output" # 去掉換行符 $ echo -e "string to output\c" # -e確保啟用了反斜杠轉移字符
eval 命令
??eval命令用於對參數進行求值,它是shell的內置命令,通常不會以單獨命令的形式存在,以下的例子展示eval的用法。eval命令就像一個額外的$,它給出一個變量的值的值。
foo=10 x=foo y='$'$x # $x就是foo echo $y # 輸出的是$foo eval z='$'$x echo $z # 輸出的是10
exec 命令
??exec命令時執行一個shell程序,也就是將當前shell替換為一個不同的程序,當前腳本程序exec命令之後的代碼都不會執行。還有一種用法是,它可以用於修改文件描述符,比較少見。
exit n 命令
??exit命令使腳本程序以退出碼n結束運行。退出碼0表示成功,1~125是錯誤代碼,126代表文件不可執行,127代表命令未找到。
export 命令
??export命令將自己的參數建為一個環境變量,而這個環境變量可以被當前程序調用的其他腳本或者程序看到。
# 以下是export2.sh #!/bin/sh echo $foo echo $bar # 以下是export1.sh #!/bin/sh foo = "foo foo foo" export bar = "bar bar bar" export2 # 調用腳本2 # 此時如果執行腳本1,我們會看到輸出bar的值,因為被聲明為環境變量,在腳本2中可見,而foo不會被輸出
expr 命令和
$( )
、$(( ))
??expr命令將它的參數作為表達式來求值,最常見用法就是進行數學運算。$(command)的結果就是執行command的 輸出結果,和兩個反引號的功能相同。
??對於表達式求值,一種更新的方法是使用
$(( ))
命令,將求值的表達式放到$(( ))
中,可以很簡單的完成數學運算,常見的表達式求值有:加+、減-、乘*、除/、取模%、與&、或|、等於=、不等!=、大於>、小於<等等。??以下的例子展示這幾個命令的用法:
$ x=1 $ x=`expr $x + 1` # 反引號是x取值為xpr $x + 1的結果 $ x=$(expr $x + 1) # $(command)具有和反引號相同的功能 $ x=$(($x + 1)) # 一種更新的方法是使用$(( ))命令,代替expr命令
set 命令和unset 命令
??set命令的作用是為shell設置參數變量。unset命令的作用是從環境中刪除變量或者函數。
#!/bin/sh echo the date is $(date) set $(date) # 將date設置為參數變量 echo the month is $2 # 輸出第二個位置參數,月份 foo = 0 unset foo # 刪除變量foo exit 0
shift 命令
??shift命令把所有的參數變量左移一個位置,使得
$2
變為$1
,$3
變為$2
,依次類推,原來的$1
被丟棄,而$0
仍將保持不變。如果指定數值參數,可以左移相應的次數。該命令的一個主要作用是用來掃描參數,如下所示。#!/bin/sh while [ "$1" != "" ]; do echo "$1" shift done exit 0
trap 命令
??trap命令用於指定在接收到相應的信號後將要采取的行動,trap命令的常見形式如下:
trap command signal # 第一個參數是接收到信號時將要采取的行動 # 第二個參數是要處理的信號名
find 命令
??find命令時一個很有用的命令,主要用於在系統中找文件,也就是文件搜索。其基本語法格式如下:
find [path] [options] [tests] [actions]
??其中,path部分很好理解,是要搜索的路徑,可以是相對路徑,也可以是絕對路徑,也可以是多個路徑。
??options部分是一些可用選項,主要有-depth(在查看目錄本身之前先搜索目錄的內容),-follow(跟隨符號鏈接),-maxdepths N (最多搜索N層目錄),-mount(或者-xdev,指不搜索其他文件系統中的目錄)
??tests是測試部分,每種測試的返回結果是true或false,主要有以下幾種:-atime N (文件在N天前被最後訪問過),-mtime N (文件在N天前被最後修改過),-name(文件名匹配),-newer otherfile(文件比otherfile要新),-type c(文件類型是c、d、f,分別對應特殊字符文件、目錄、普通文件),-user username(文件的擁有者是username)。
??註意:tests測試可以組合使用,有三個組合命令:-and,-or,-not
??actions部分是匹配之後要執行的動作,比如:-exec command(執行一條命令,最常見),-print(打印)等等。
$ find / -name test -print $ find / -mount -name test -print $ find . -newer while2 -print $ find . -newer while2 -type f -print
grep 命令
??grep命令是通用正則表達式解析器,通俗的說,find命令在系統中找文件,grep命令在文件中找字符串,一種常用的做法是將grep作為傳遞給-exec的一條命令。
??grep命令的基本語法如下:
grep [options] PATTERN [FILES]
??options是一些主要選項,常用的有-c (輸出匹配行的數目)、-E(啟用擴展表達式)、-h(取消每個輸出行的普通前綴)、-i(忽略大小寫)、-l(只列出包含匹配行的文件名)、-v(取反,搜索不匹配行)。
??PATTERN主要是一些匹配模式,常用正則表達式來表示,關於正則表達式的內容參照另一篇博文正則表達式。
grep in words.txt grep -c in words.txt words2.txt grep -c -v in words.txt words2.txt grep "e$" words2.txt grep "a[[:blank:]]" words2.txt grep -E [a-z]\{10\} words2.txt
總結
??Shell程序設計作為一種腳本語言,在Linux系統中有廣泛的應用,本文記錄了關於Shell程序設計的基礎語法知識和常用命令,方便查詢,熟練使用shell也需要經常實踐,這對於完成一些較簡單的編程任務很有幫助。另外,shell程序在Linux中海油一個可視化工具:dialog,由於不常使用,這裏不再介紹。
【Shell編程】Shell基本語法