[Linux] BASH程序設計

1. 控制結構shell


1.1 if...thenexpress

if...then控制結構的語法以下:數組

if test-command
then
  commands
fi

if 語句測試test-command返回的狀態,並基於這個狀態轉移控制。if結構的結束由fi語句標記,例如:bash

echo -n "Word 1: "
read word1
echo -n "Word 2: "
read word2
if test "$word1" = "$word2"
then
  echo  "Match"
fi

在BASH中,test是一個內置命令,也就是說它是shell的一部分,同時還有一個單獨的工具test。對於數值比較測試有如下幾種測試選項:-ne(不等)、-eq(等於)、-gt(大於)、-ge(大於等於)、-lt(小於)以及-le(小於等於)。對於字符串比較能夠用=(等於)、!=(不等於)來進行測試比較。特殊變量"$#"表示命令行參數的個數,例如:ide

if test $# -eq 0
then
  echo "Error"
  exit 1
fi

可使用test來判斷文件參數狀態或兩個文件參數的關係,若是test內置命令帶有"-f"選項和一個參數(第1個參數$1),那麼它就可用來檢查參數所指定的文件,例如:函數

if test -f "$1"
then
  echo "$1 is  a regular file"
else
  echo "$1 is not a regular file"
fi
test內置命令的選項
選項
功能
-d 檢查文件是否存在以及該文件是不是文件目錄。
-e 檢查文件是否存在。
-f 檢查文件是否存在以及該文件是不是一個普通文件。
-r 檢查文件是否存在以及該文件是否可讀。
-s 檢查文件是否存在以及該文件是否大於0字節。
-w 檢查文件是否存在以及該文件是否可寫。
-x 檢查文件是否存在以及該文件是否可執行。

"[]"爲test的同義詞,能夠把test的參數用方括號括起來,例如:工具

if [$# -eq 0]
then
  echo "..."
  exit 1
fi


1.2 if...then...else oop

在if結構中引入else語句使其爲分支結構,語法以下:測試

if test-command
then
  commands
else
  commands
fi

如同換行符同樣,分號能夠結束一條命令,所以,能夠把then與if放在同一行,並在then的前面加一個分號,例如:this

if test-command; then
  commands
else
  commands
fi

若是test-command返回true狀態,if結構執行then和else語句之間的命令,而後把控制轉向fi後面的語句。若是test-command返回false狀態,if結構執行else語句後面的命令。


1.3 if ...then...elif

if...then...elif控制結構的語法以下:

if test-commands
then
  commands
elif test-command
then
  commands
else
  commands
fi

elif語句組合了if語句與else語句,使得能夠嵌套多個if...then...else結構。else語句和elif語句之間的差異在於每一個else語句必須與一個fi語句配對,而多個嵌套的elif語句只須要一個fi語句。


1.4 for...in

控制結構for...in的語法以下所示:

for loop-indexin argument-list
do
  commands
done

for...in結構把argument-list中的第1個參數賦給loop-index變量,並執行do和done語句之間的命令。 在腳本把控制傳給done語句以後,結構把arguments-list的第2個參數賦給loop-index變量並再一次執行do和done之間的命令。


1.5 for

for控制結構語法以下:

for loop-index
do
  commands
done

在for結構中,loop-index用命令行參數中的每一個參數值取代,一次執行一個。除了loop-index變量的取值來源,for結構與for...in結構是相同的。for結構一般依次根據每一個參數執行一個命令序列。


1.6 while

while控制結構的語法形式以下:

while test-command
do
  commands
done

只要測試條件的返回值爲真,while結構語句就要執行do與done語句之間的命令。在每次循環以前,while結構都要檢查測試條件。一旦測試語句的返回值爲假,while結構語句就把控制傳遞到done語句以後的程序段。


1.7 until

until語句與while語句的語法結構類似,區別只在於until在語句的結束測試。until的語法結構以下:

do
  until test-commands
done


1.8 break與continue

利用break和continue語句能夠在for、while或until語句中產生中斷。break語句能夠跳出循環,把控制直接轉移到done語句以後的內容。continue語句把控制傳到done語句, 並繼續執行循環。


1.9 case

case控制結構是一種多分支選擇機制,具體選擇哪一個分支依賴於測試串和某個分支類型之間的匹配狀況,case控制結構的語法以下:

case test-string in
  pattern-1)
commands-1
;;
  pattern-2)
commands-2
;;
  pattern-3
commands-3
;;
esac

case結構中的匹配類型相似於一個模糊文件引用,實際上匹配類型能夠包括表中的任何字符或字符串:

匹配類型
類型
功能
* 匹配任意字符串,用做默認的case匹配。
? 只匹配單個字符。
[...] 定義一個字符類,對處在方括號的每一個字符依次進行單字符匹配。
| 分離帶有選擇的選項,這些選項知足case結構的一個特別的分支。


1.10 select

select控制語句首先顯示一個菜單,而後根據用戶選擇給變量賦予相應的值,最後執行一系列命令。select控制結構的語法形式以下:

select varname [in arg...]
do
  commands
done

select結構顯示的內容爲arg條目的菜單。假如忽略鍵盤輸入和參數列表,select控制語句會用位置參數來取代arg條目。


2. 參數和變量


2.1 文件描述符

一個進程不管從文件中讀內容仍是向文件中寫內容以前必須先打開這個文件。當一個進程打開文件時,Linux中常給這個文件分配一個數字,即文件描述符。一旦打開某個文件,進程不管是讀或者是寫該文件都要靠文件描述符來進行操做。當進程再也不須要該文件時,它必須關閉該文件同時也要釋放文件描述符。

一個典型的Linux進程在啓動時包括3個已打開的文件:標準輸入、標準輸出和標準錯誤輸出。在bash中使用exec內置命令打開文件,例如:

exec n> outfile
exec m< infile

第一行是打開一個輸出文件outfile,並給它賦予文件描述符n,第二行是打開一個輸入文件infile,並給它分配文件描述符m。符號"<&"的做用是複製一個輸入文件描述符,符號">&"的做用是複製一個輸出文件描述符。能夠經過把兩個文件描述符指向同一個文件的方法來複制文件描述符,例如:

exec n<&m


2.2 數組變量

bash支持一維數組做爲變量。數組的下標是整數並以數字0做爲起始,格式以下:

name=(element1 element2...)

能夠按照以下方式引用數組中的某個元素:

echo ${NAMES[1]}

下標"[*]"和"[@]"的做用都是提取出整個數組元素,可是當它們加上雙引號使用時工做機制卻不一樣。"@"符號的含義是把原數組的內容複製到一個新數組中,生成的新數組和原來是同樣的。可是"*"符號是把原數組中的全部元素當成一個元素複製到新數組中,生成的新數組只有一個元素,例如:

A=("${NAME[*]}")
B=("${NAME[@]}")

把操做符"${#NAME[*]}"放在一個數組變量的前面能夠返回數組中元素的個數,例如:

echo ${#NAME[*]}

把上面操做符中的"*"符號替換爲數組的下標就能返回數組中對應元素內容的長度,例如:

echo ${#NAME[1]}

也能夠將數組下標放在賦值語句的左邊以便對相應的數組元素進行賦值,例如:

NAME[1]=John


2.3 變量局部性

進程在遇到默認變量時,通常當作是聲明位置處的局部變量。除非將變量聲明爲可訪問的全局變量,不然shell腳本不能訪問用戶在登陸shell中聲明的變量。在bash下,export命令可使父進程的變量對子進程來講是可以使用的,例如:

export name=John
echo "$name"
#subscript
echo "$name"

因爲函數運行的環境一般與其被調用的環境相同,因此其中變量是顯示的被shell和調用它的函數一塊兒共享的,例如:

function name() {
  echo $myname
  myname=John
}
myname=Marry
name #Marry
echo $myname #John

在函數中使用局部變量,可使用typeset內置命令,例如:

function name() {
  typeset myname
  myname=John
  echo $myname  
}
myname=Marry
name #John 
echo $myname #Marry


2.4 特殊參數

shell把執行shell的進程的PID號存儲在特殊參數"$$"中,例如:

echo $$

把PID號包含在一個文件名中對於建立一個惟一的文件名是很是有益的,這種方法經常用在shell腳本中用來建立臨時文件的名稱。

後臺運行的進程的PID號存儲在符號"$!"中,例如:

sleep 60 &
echo $!

一個進程不管因爲何種緣由中止運行,它都要向父進程返回一個exit狀態,返回的狀態能夠被認做是條件碼或者返回碼,"$?"中存儲着上一個命令的返回狀態碼,例如:

ls
echo $?


2.5 位置參數

參數"$#"保存了命令行上除命令自身以外的參數的個數,例如:

echo "This script was called with $# arguments."

參數"$0"中保存了用來執行程序命令的名稱,該參數被設置爲0是由於它出如今命令行上第1個參數的前面,例如:

echo "The command used to run this script is $0"

命令行上的第1個參數由"$1"替換,第2個由參數"$2"替換,一直到"$n",一旦n的值超過9,數字兩邊就要加上大括號,例如:

echo "First 5 arguments are $1 $2 $3 $4 $5"

變量"$*"包含了全部的命令行參數,例如:

echo "All arguments are $*"

參數"$*"和"$@"除了它們在加上雙引號用法不一樣外,其餘的用法都相同。使用參數"$*"只能產生一個參數,而參數"$@"則生成一串參數,其中每一個位置參數仍然是一個單獨的參數。


2.6 左移命令行參數

利用shift內置命令能夠移動每一個命令行參數,向左移動時,第1個參數被丟棄,第2個參數變成第1個參數,依次類推。已經丟棄的命令沒法找回,例如:

echo "arg1=$1 arg2=$2 arg3=$3"
shift
echo "arg1=$1 arg2=$2 arg3=$3"


2.7 初始化命令行參數

set命令是用來初始化命令行參數變量的。set命令把set後跟的一個或幾個參數賦值給位置參數,這些位置參數以$1打頭,例如:

set this is it
echo $1 $2 $3


2.8 擴展空變量和未設置變量

表達式${name}擴大爲變量name的值。若是name變量爲空或尚未設置,bash就將${name}擴展成一個空串。能夠經過給變量加上一個修飾符來選擇幾個選項:變量使用默認值、使用默認值並將其賦給變量、顯示錯誤。

修飾符":-"使用一個默認值來替代那些空的或者沒有賦值的變量,格式以下:

${name:-default}

例如:

${APP_PATH:-/home/test}

修飾符":-"不能改變理的值,但若是但願修改腳本中空變量或未賦值變量的默認值,修飾符":="能夠實現這個功能,格式以下:

${name:=default}

shell按照擴展表達式${name:-default}的方式來擴展表達式${name:=default},同時把變量name的值設置爲default的值。

shell腳本中經常使用冒號":"後跟擴展表示符":="來給任意一個空變量或者未賦值的變量賦值。冒號一般給命令行上其後的符號賦值而不會去執行後面的命令,格式以下:

: ${name:=default}

有時,經過設置默認的變量值不能給腳本中某些變量提供一個合理的值,這時":?"修飾符就會顯示出錯誤信息並停止腳本的執行同時返回退出碼1,格式以下:

${name:?message}


3. 內置命令


3.1 type

使用type命令能夠顯示出系統命令的相關信息,例如:

type cat echo


3.2 read

經過使用read命令,腳本能夠接受用戶的輸入並將輸入信息存入到用戶建立變量中。經過使用read命令,腳本能夠接受用戶的輸入信息儲存到變量中,例如:

read myvar
echo "Entered: $myvar"

read命令有一些特性可使使用read變得更加方便。若是不想指定一個變量來保存read的輸入內容,bash會把用戶的輸入放在一個名爲REPLY的變量中。經過用選項-p來顯示用戶提示,例如:

read -p "Go ahead:"
echo "Entered: $REPLY"

若是用戶輸入大於read擁有的變量數,read將按照變量的順序先給每一個變量分配一個非空內容,到最後一個變量時,把剩下的內容所有分配到這個變量中。


3.3 exec

使用exec內置命令有兩個主要的目的:第1個是使用它能夠不用建立新進程來執行一個命令,第2個是使用它能夠重定向來自shell腳本內部的文件描述符。通常假如shell執行的命令不是來自shell內部,那麼執行這個命令就會建立一個新的進程,這個新進程繼承來自父進程的環境變量而不會繼承父進程中沒有使用export導出的變量。相反exec執行命令時常覆蓋當前的進程。

可使用以下語法來使exec運行一個命令:

exec command arguments

因爲exec並不建立新進程,因此執行速度很是快,而且因爲exec不能把控制返回到原程序中,因此一般把它做爲最後一個命令。

使用exec也能夠把來自shell腳本內部的文件描述符的信息重定向到其餘文件中,例如:

exec > outfile 2> errfile

當以這種方式使用exec命令時,當前的進程不會被新的進程取代。


3.4 trap

在Linux中,信號能夠用來報告用戶產生的中斷,還能夠用來報告諸如錯誤的系統調用、管道中斷、非法指令和其餘情況等。使用trap內置命令來捕獲一個或多個信號,以便於用戶在收到一個特殊的信號時採起相應的動做。

能夠按以下語法使用trap命令:

trap ['commands'] [signal]

可選項command指出了當腳本在捕獲到由signal指定的信號後應採起的指令。signal能夠是信號的名字或者信號的編號,如INT或2。若是沒有command命令,那麼trap命令就會把trap重置到初始化狀態。

當執行過commands的內容後,shell會恢復執行commands命令離開處的腳本。在收到一個信號後,若是用戶想使用trap阻止腳本退出但又不想運行任何一個顯式命令,能夠給commands指定一個空串,例如:

trap '' 15


3.5 kill

kill內置命令用來給一個進程或者做業發送信號,kill命令的語法格式以下:

kill [-signal] PID

signal是信號的名字或者信號的編號,PID是要接收信號的進程號,可使用%n的形式指定一個做業編號來替代PID,若是省略了signal,kill命令就發出一個TERM信號,例如:

kill -TERM %1


3.6 getopts

getopts內置命令用來解析命令行參數,語法結構以下:

getopts optstring varname [arg...]

其中,optstring是合法的字母選項列表,varname變量保存了每次接收的選項的值,arg是即將被處理的可選參數。若存在arg參數,getopts就去處理命令行參數,若optstring以冒號":"做爲開始,則由腳本負責產生錯誤信息,不然就由getopts產生錯誤信息。

getopts命令使用變量OPTIND(選項索引)和OPTARG(選項參數)來保存和選項相關的值。當shell腳本啓動時,OPTIND的值被設置爲1,之後每次當getopts命令發現一個參數,它就增長OPTIND的值,該值與下一個將要被處理的選項的索引相等。若是選項中含有參數,bash就把參數的值賦給變量OPTARG。

爲了指定某個選項含有參數,在optstring中相應的字母后面加上一個冒號,例如:

while getopts do:t:r arg
do
  case $arg in
    d) echo "-d";;
    o) echo "-o : $OPTARG";;
    t) echo "-t : $OPTARG";;
    r) echo "-r";;
  esac
done


4. 表達式


4.1 算術表達式

bash可以處理算術賦值,並可以對各類算術表達式求值,shell中有不少方法能夠用來進行算術賦值,其中一種是使用let,例如:

let "VALUE = VALUE * 10 + NEW"

let語句中不須要在變量前面加美圓符號,但必須將單個的變量或者帶有空格的表達式用雙引號引發來。因爲let的每一個參數被解釋爲一個獨立的表達式,因此能夠在一行上給多個變量進行賦值,例如:

let "COUNT = COUNT + 1" VALUE=VALUE*10+NEW

能夠利用((expression))的語法結構來同時表示算術表達式和邏輯表達式,例如:

if ((30 < age && age < 60)); then
  echo "$age"
fi


4.2 邏輯表達式

條件表達式的語法形式以下:

` expression `

在expression中必須在變量的名字前面加上美圓符,執行該表達式的結果與命令test同樣,返回的是一個狀態,例如:

if [[ 30 < $age && $age < 60 ]]; then
  echo "$age"
fi

也可使用test命令的關係比較符:"-gt"、"-ge"、"-lt"、"-le"、"-eq"和"-ne"。操做符">"和"<"按字母順序比較字符串,操做符"="進行類型匹配比較,好比"[[ artist = a* ]]"返回爲真。


4.3 字符串模式匹配

bash提供了可操做路徑名字符串以及其餘字符串類型匹配操做符,這些操做符能夠從字符串的前綴或後綴中刪去字符串。

字符串操做符
操做符 功能
# 去除最小匹配前綴。
## 去除最大匹配前綴。
% 去除最小匹配後綴。
%% 去除最大匹配後綴。

這些操做符的語法形式以下:

${varname op pattern}

op是上表中的操做符,pattern是一個匹配類型,例如:

MYFILE=/usr/local/src/test.c
echo ${MYFILE%.c}


4.4 操做符

算術擴展和算術賦值使用了和C語言相同的語法、操做符的運算優先級以及表達式的關聯關係。下表按照優先等級遞減的順序列出了這些操做符:

操做符
操做符類型
功能
後置 var++ 後置加
後置 var-- 後置減
前置 ++var 前置加
前置 --var 前置減
一元 - 一元減
一元 + 一元加
取反 ! 布爾取反
取反 ~ 二進制取反
取冪 ** 冪指數
乘法 * 乘法運算
除法 / 除法運算
取模 % 取模運算
加法 + 加法
減法 - 減法
二進制移位 << 左移
二進制移位 >> 右移
比較運算符 <= 小等於
比較運算符 >= 大等於
比較運算符 < 小於
比較運算符 > 大於
相等 = 相等
不等 != 不相等
二進制位運算符 & 二進制AND運算
二進制位運算符 ^ 二進制XOR運算
二進制位運算符 | 二進制OR運算
布爾 && 布爾AND運算
布爾 || 布爾OR運算
條件賦值 ? : 三元操做符
賦值 =、*=、/=、%=、+=、-=、<<=、>>=、&=、^=、|=賦值操做
逗號 , 逗號操做符

管道操做符的優先級比全部操做符都高,例如:

cmd1 | cmd2 || cmd3 | cmd4 && cmd5 | cmd6

前置和後置操做符要與變量結合在一塊兒使用,例如:

echo $((--N+3))

取模操做符取出第1個操做數被第2個除以後的餘數,例如:

echo $((15%7))

使用布爾操做符所得的結果要麼是0,要麼是1,布爾操做符被稱爲短路操做符,若是僅僅經過左邊的操做數就能夠得出最終的結果,那麼右邊的操做數就能夠不用賦值。

變量"$?"中保存了前面命令執行後的退出狀態,例如:

true || false && false
echo $?
相關文章
相關標籤/搜索