一、SHELL語句流程控制
1、過程式編程語言的流程控制
順序執行
選擇執行
循環執行
2、順序執行
順序執行則是最簡單的流程,按照輸入指令的順序逐條執行
3、選擇執行
就是根據一些判斷的語句,選擇性的執行某些分支命令,不執行某些命令。例:if語句;case語句
4、循環執行
根據一些條件來判斷,是執行true執行true那一部分,是false則退出循環語句。
二、條件選擇if語句
If選擇語句分爲兩種,一種就是單分支if,一種就是多分支語句。If語句支持嵌套。
1、單if語句
語法格式
if 條件 ;then
命令塊1(可以是多個命令)
else
命令塊2(可以是多個命令)
fi
如果符合該條件,則執行命令塊1處的命令不執行命令塊2處的命令,如果條件不符合,則跳過命令塊1處的命令不執行,直接執行命令塊2處的命令,條件多爲一個判斷命令,返回值爲0(true)執行命令塊1,返回值爲1(false)執行命令塊2
例如:
if $(id $1 &> /dev/null);then
echo "$1 alread exist"
exit 1
else
useradd $1
fi
2、多if語句
語法格式:
If 條件1 ;then
命令塊1
elif 條件2 ;then
命令塊2
elif 條件3;then
命令塊3
……
elif 條件n;then
命令塊n
else
命令塊n+1
fi
執行順序是,先判斷條件1,返回值爲true則執行命令塊1,然後退出if語句
如果條件1,返回值爲false,則繼續判斷條件2,返回值爲true,則執行命令塊2,然後退出if語句,若條件2 返回值爲false,則繼續判斷條件3……,如果所有條件返回 值都爲false,則執行else後面的命令塊n+1,然後退出if語句
例如:
#!/bin/bash
if [ $1 -lt 3 ];then
echo redhat
elif [ $1 -eq 3 ];then
echo green
elif [ $1 -gt 3 -a $1 -lt 5 ];then
echo yellow
else
echo white
fi
三、條件判斷:case語句
case條件判斷語句是一個多分支結構的,適合用於分支多的情況,而且比if多分支語句更加簡潔一點,但是case語句不可嵌套。
1、case語句
case 變量 in
變量值1)命令塊1;;
變量值2)命令塊2;;
變量值4)命令塊3;;
變量值n)命令塊n;;
esac
執行順序是,先調用一個變量,然後判斷變量值是否符合變量值1,如果相等,則執行命令塊1,如果不符合則判斷變量值2,……一直到變量值n,如果都不符合則則什麼都不執行或執行*後面的操作,*代表除了上面值所有值
例如:
case $1 in
1)echo redhat;;
2)echo yellow;;
3)echo green;;
*)echo blue;;
esac
四、循環語句
循環執行的特點:
將某代碼段重複運行多次
重複運行多少次:
循環次數事先已知
循環次數事先未知
有進入條件和退出條件
1、for循環語句
for循環語句的格式1:
for 變量名 in 變量值列表;do
循環體
done
執行機制:依次將列表中的元素賦值給“變量名”;每次賦值後即執行一次循環體;直到列表中的元素耗盡,循環結束,for循環是先賦值在判斷,
列表生成方式:
1.直接給出列表
2.整數列表
(a){start..end}
(b) $(seq [start [step]] end)
3.返回列表的命令
$(cmd)
4.使用glob,如*.sh
5.變量引用
$@,$*
2、While循環語句
while循環語句語法格式:
while 循環控制條件 ;do
循環體
done
執行機制:進入循環之前,先做一次判斷;每一次循環之後會再做判斷;條件爲true則執行一次循環;知道條件測試狀態爲false終止循環。因此循環控制條件一般應該有變量,而變量的值會再循環體中發生變化,而且變量的第一次賦值應該在循環的前面,否則無法進行判斷,
使用while創建無限循環
while true;do
循環體
done
3、Until循環語句
until循環語句格式:
until 循環控制條件;do
循環體
done
執行機制:until跟while語法格式差不多,而且都是先判斷再循環,不同的是while是條件測試返回值爲真進入循環,爲假則退出循環,而until則是條件測試返回值爲假則進入循環,爲真時退出循環
使用unti創建無限循環
until false;do
循環體
done
五、Shell中continue和break的用法與區別
1、continue
Continue用於循環體中用於提前結束本輪的循環,不再執行後續的命令,而直接進入下一輪循環,而不是將整個循環體都退出,比如在循環體內寫入某一個條件,當滿足這一條件是,執行continue這個命令,就不會再執行循環體內continue後面的那些命令了,然後直接進入下一輪的循環,一直到循環結束。
示例:編寫一個腳本shuzi.sh,輸出1到10 的數字內容如下:
如圖所示,腳本中輸出1到10的數字,但是當在第5輪的循環中,符合條件i等於5將會執行continue命令,而本輪循環中後面的echo $i就不會再執行了,而是退出這第五輪的循環,直接進入下一輪循環。所以這1到10 的數字中不會輸出5輸出結果如下圖:
2、Break
Break用於循環體內,用退出整個循環體,不再執行後續的循環,比如本來需要執行10次循環的,但當符合第5層循環時,符合某以條件而執行了break命令,那麼就會退出當前的循環體,而後續的循環也不再執行了。
例:編寫一個腳本shuzi2.sh,輸出1到10的數字
如圖所示,腳本中依舊是輸出1到10的數字,不同的就是,當i等於5時執行break命令,如此就會退出這個循環,而不輸出5及之後的所有數字,執行結果如下:
3、break命令在多層嵌套循環中的使用
在嵌套循環中,也就是多層循環體中break僅退出包含break命令的當前循環,而不會退出其他的循環體或本層循環的上一層循環。
例:編寫一個腳本qt.sh,打印九九乘法表
上圖中是有兩層循環體的,這就是循環的嵌套使用,循環中包含着一個循環,在這種情況中,break在裏面那一層循環中,當符合m不小於i時就會執行break,也就是在第一輪循環中i=1;m=1,然後判斷m是否小於等於i ,返回值爲true,輸出1x1=1;然後執行裏面的那個循環的第二輪,m++=2,i=1,判斷m是否小於等於i,返回值爲false,執行break,退出了當前循環,但是外面那一層循環體並不會退出,會依舊循環執行i++=2,m=1,判斷m是否小於等於i,返回值爲true,輸出1x2=2;然後執行內循環的第二輪循環i=2,m++=2,再次判斷m是否小於等於i,返回值爲true,輸出2x2=4;執行內循環的第三輪,i=2,m++=3,判斷m是否小於等於i,返回值爲false,執行break,退出內循環,繼續外循環的第三輪循環......執行 結果如下圖:
4、Break與continue的區別
區別在於continue是退出某一循環體的某一輪的循環,但是循環體本身不退出,不、妨礙後續的循環,break是果斷退出當前的整個循環體,使當循環體不再循環。
六、循環控制shift命令
shift用於將參數列表左移指定次數,缺省爲左移一次,。參數列表一旦被移動,最左端的那個參數就從列表中刪除。While循環遍歷位置參數列表時,常用到shift
用法:shift [n]
如下圖中所示,編寫一個腳本,使用while循環顯示所有的參數時,如果不使用shift,則參數數量不會發生變化,使其成爲了死循環,一直顯示所有的參數
如下圖中所示,在循環中添加一個語句shift,則會在每次循環時,將參數從左向右移一次,而且移動後,坐左邊的那個參數就被刪除了,也不再顯示,
示例:顯示所有的參數,一次顯示一個參數,一個參數佔一行
七、特殊用法
1、while的特殊用法
While循環還有一個特殊用法,可以遍歷文件的每一行,下面我們編寫一個腳本和一個文件如下圖中,使用腳本讀取gushi中的每一行美容並顯示出來:
然後我們執行以下看看結果,這個執行步驟就是,依次讀取/app/gushi文件中的每一行,且將值賦值給變量line,直到把所有的行都賦值給變量然後才結束循環;這個方法就有點跟for循環相似了,給變量賦多個值,每一個值執行一次循環,知道把所有的值讀取完循環就結束,
2、雙小括號的用法
雙小括號使用方法,即((...))格式
1.雙小括號可以用於算數運算
var=$((算術表達式))
2.雙小括號也可以使bash shell實現c語言風格的變量操作
((i++))
3.For循環的特殊格式
for ((控制變量初始值;條件判斷表達式;控制變量的修正表達式))
do
循環體
done
控制變量的初始化值:僅在運行到循環的第一輪循環會執行一次,
條件判斷表達式:在每一輪循環執行開始之前都會執行一次,這個判斷表達式的值爲true則執行循環一次,如果值爲false,則退出這個循環。
控制變量的修正表達式:每輪循環結束會先進行控制變量修正運算,而後在做條件判斷,這裏通常會使變量的值發生改變。
示例:如下圖圖1中所示,第一輪循環i=1,然後輸出1,;第二輪開始先給執行i++,然後i的值就是2了,再判斷i是否小於等於10,值爲true則執行一次循環體輸出i的值2;然後進入第三輪循環,先執行i++,這個時候i的值編程了3,在判斷i的值是否小於等於10,值爲true執行循環體輸出i的值3,進入下一輪循環……進入第10輪循環,先執行i++,i的值是10 ,判斷i是否小於等於10,值爲true,執行循環體輸出i的值10;進入第十輪循環,執行i++,i的值爲11,判斷i是否小於等於10,值爲false,退出循環。所以雖然最後只輸出到10,但是因爲for循環是先執行控制變量的修正表達式,然後在執行判斷表達式判斷值是真是假再決定是否繼續執行循環體,所以循環體結束後i的值已經是11了,如下圖圖2顯示,在循環之外再顯示一次i的值
八、Select循環與菜單
Select的特點:
1.Select循環主要用於創建菜單,按數字順序排列的菜單項將顯示在標準錯誤上,並顯示PS3提示符,等待用戶輸入
2.用戶輸入菜單列表中的某個數字,執行相應的命令
3.用戶輸入被保存在內置變量REPLY中
4.Select是個無限循環,因此可以使用break命令退出循環,或用exit命令終止腳本,也可以按Ctrl+c退出循環
5.Select經常跟case聯合使用
6.與for循環類似,可以省略in list ,此時使用位置參量
7.Select的list的參數值以空格分隔,可以是英文,也可以是中文,如參數中包含有空格,得加雙引號
8.Select循環可以嵌套
Select的語法格式:
select 變量名 in list
do
循環體命令
done
示例:
1.編寫一個簡單的select菜單
如上圖中所示,我編寫了一個簡單的菜單系統,選擇要吃的飯;輸入提示符可以更改變量PS3的值,在想要執行變量爲其中某個值時要做的事,需輸入相應的序號,當輸入一個沒有的序號或其他字符時則輸出一行空行繼續循環,而且這個循環一旦執行是不能退出的,輸入exit或quit都沒有用,要退出還是得Ctrl+c強制退出。
2.使用select和case語句編寫一個菜單腳本,燴麪6元,拉麪7元,重慶小面8元;當用戶什麼也不吃的時候退出腳本,輸入其他任何選項或字符時退出select循環。
3.自定義給以上的腳本中select循環,設置輸入提示符爲“請選擇您想吃的飯”
4.使用select語句嵌套,更改上面的腳本一個腳本,使其在選擇燴麪的時候,可以選擇大份或小份,
5.使用case和select語句的結合,利用REPLY變量編寫菜單
九、信號捕捉trap
1、進程信號
之前學過進程管理的應該都知道kill命令,進程管理就是向進程發送一些控制信號,來完成對進程的管理控制,我們可以通過kil -l或trap -l來查看當前系統可用的信號
常用的信號有:
SIGHUP:1 通知進程重讀配置文件以讓新的配置生效,無需重新啓動進程
SIGINT:2終止正在運行中的進程,相當於鍵盤組合鍵Ctrl+c
SIGQUIT:3 相當於Ctrl+\
SIGKILL:9 強行終止正在運行中的進程
SIGTREM:15 強行終止正在運行中的進程
SIGSTOP:19 暫停前臺的進程,使其在後臺休眠,相當於ctrl+z
SIGCONT:18 繼續運行指定的進程
信號的表示方法:
1.完整名稱,例如:SIGINT
2.簡寫名稱,例如:INT
3.數字表示,例如:2
2、trap的作用
Trap的作用就在於能夠捕捉這些信號,使在使用這些信號對進程進行管理時,不執行原操作,而執行我們所指定的操作,比如在執行一個腳本時按Ctrl+c或者執行命令kill -19會終止或中斷這個腳本的運行,但是我想屏蔽掉這個信號,使其在運行過程中這個進程是殺不死的,trap就可以實現這個功能,但是有一個信號無法屏蔽,那就是9 SIGKILL,這個是強制性的終止進程,其它的是可以屏蔽掉的。
Trap的語法:
trap ‘觸發指令’ 信號
自定義進程收到系統發出的指定信號後,將執行觸發指令,而不會執行原操作,當單引號內爲空什麼都沒有時,就是什麼都不做,僅忽略那個信號。
trap “ ” 信號 :忽略信號的操作
trap “ - ” 信號 :恢復信號的操作
trap -p :列出自定義信號操作
3、示例的腳本
每隔0.5秒輸出一個數字,忽略信號int,也就是Ctrl+c這個信號,前10個數字忽略輸出“根本就停不下來”,10至20什麼都不做,20至30之間恢復信號的操作
十、函數
1、函數的特性
函數function是由若干條shell命令組成的語句塊,實現代碼重用和模塊化編程
它與shell程序形式上是相似的,不同的是它不是一個單獨的進程,不能獨立運行,而是shell程序的一部分
函數和shell程序比較相似,區別在於:
Shell程序在子shell中運行
而shell函數在當前shell中運行,因此在當前shell中,函數可以對shell中變量進行修改
Shell中的代碼都是被逐行讀取並運行的,而函數部分的命令在被讀取的時候是不會被運行的,只有在調用函數的名字時,纔會運行已經定義好的函數中的那一部分代碼,適用於當某一段語句塊需要多次運行的情況下,這些命令就需要多次被讀取執行,而調用函數只用編寫一遍,後面需要用到的地方調用函數名即可,這樣節省我們編寫shell腳本的時間
函數由兩部分組成:函數名和函數體
函數名部分是用來調用函數體部分的命令
函數體部分就是由需要被多次執行的命令語句塊組合成的
2、函數的語法:
語法一:
function_name (){
...函數體...
}
語法二:
funtion function_name {
...函數體...
}
語法三:
function function_name () {
...函數體...
}
Function_name :函數名
{}中間部分就是函數體,在調用函數名的時候回被執行的命令塊
示例:使用函數編寫一個腳本,計算從1加到100的和
3、函數的使用
函數的定義和使用:
可在交互式環境下定義函數
可將函數放在腳本文件中作爲它的一部分
可放在只包含函數的單獨文件中
函數的調用:
調用:給定函數名
函數名出現的地方,會被自動替換爲函數代碼
函數的生命週期:被調用時創建,返回時終止
4、函數的返回值
函數有兩種返回值:
1.函數的執行結果返回值:
使用echo等命令進行輸出
函數體中調用命令的輸出結果
2.函數的退出狀態碼:
默認取決於函數中執行的最後一條命令的退出狀態碼
自定義退出狀態碼,其格式爲:
return 從函數中返回,用最後狀態命令決定返回值
return 0 無錯誤返回
return 1-255 有錯誤返回
函數的執行結果返回值就是函數體內的所有命令的執行結果的輸出結果
函數的退出碼就是執行完函數後,返回的一個值,就跟在終端上執行的每一個命令都會有一個返回值一樣,執行的命令爲0,說明這個命令是正確的,並執行成功了,爲除了0以外的所有值皆爲錯誤,錯誤的返回值再0-255之間,函數也是這樣,在函數中沒有指定返回值,那麼這個函數的返回值皆由這個函數的函數體內的最後一條命令的返回值爲函數的返回值,要如果指定函數的返回值,就在函數體內最後一行編輯return命令,自定義函數返回值,return命令一定要在最後一行,因爲函數中讀取到return命令後,那麼其後面的命令就不不會再執行,然後退出函數,函數中return命令就好像腳本的exit命令會退出這個腳本,return命令會退出這個函數,但是函數外的命令依舊會執行。
示例:
5、交互式環境下定義和使用函數
交互式環境下定義函數就是不在腳本中定義的函數,在終端上定義的函數
語法:
function_name (){
> 函數體
> }
定義函數後,調用函數是在命令行界面輸入函數名即可,就會執行函數體部分的所有命令
該函數在定義後將一直保留到用戶從系統推出,或執行了卸載函數的命令:
unset 函數名
示例:
6、在腳本中定義及使用函數
函數在使用前必須定義,因此穎將函數定義放在腳本開始部分,直至shell首次發現它後才能使用,調用函數僅使用函數名即可
示例:
7、使用函數文件
可以將經常使用的函數存入函數文件然後將函數文件載入shell
文件名可以任意選取,但最好與相關任務有某種聯繫。例如:functions.main
一旦函數文件載入shell,就可在命令行或腳本中調用函數。可以使用set命令查看所有定義的函數,其輸出列表包括已經載入shell的所有函數
若要改動函數,首先用unset命令從shell中刪除函數。改動完畢後,再重新載入此文件
創建函數文件依舊需要在首行編輯/bin/bash
函數文件不需要加執行權限就可以用
一個函數文件中可以定義多個函數,不一定只能有一個,當載入這個函數文件時,這個文件的所有函數都會被載入,可以隨時調用使用
8、載入函數
函數文件創建好後,要將它載入shell,定位函數文件並載入shell的格式:
. Filename 或 source filenname
注意:<點><空格><文件名> :這裏的文件名要帶正確路徑
示例:
9、檢查載入函數
使用set命令檢查函數是否已載入,set命令將在shell中顯示所有的載入函數
10、執行shell函數
要執行函數,簡單的鍵入函數名即可
11、刪除shell函數
對函數做一些改動後,需要先刪除函數,使其對shell不可用,使用unset命令完成刪除函數,使用unset後,再鍵入set命令,該函數將不再顯示
命令格式:unset 函數名
環境函數:
在當前shell中定義的函數,在子bash中是不可用,想要這個函數在其子進程中也可用可以在定義函數後再使用export -f 函數名,聲明全局函數
聲明:export -f 函數名
查看:export -f 或declare
示例:
12、函數參數
示例:
13、函數變量
函數變量就是在函數中定義的變量
變量作用域:
環境變量:當前shell和子shell有效
本地變量:只在當前shell進程有效,爲執行腳本會啓動專用子shell進程;因此,本地變量的作用範圍是當前shell腳本程序文件,包括腳本中的函數
局部變量:函數的生命週期;函數結束時變量被自動銷燬
注意:如果函數中有局部變量,如果其名稱同本地變量,使用局部變量
在函數中定義局部變量的辦法
local NAME=VALUE
示例:
14、函數遞歸
函數內是可以自己調用其他的函數或函數本身,
函數遞歸:
函數直接或間接調用自身
注意遞歸層數
示例:利用函數達成無限循環的目的
15、Fork×××
Fork×××是一種惡意程序,它的內部是一個不斷在fork進程的無限循環,是指是一個簡單的遞歸程序,由於程序時遞歸地,如果沒有任何限制,這會導致這個簡單的程序迅速耗盡系統裏面的所有資源
函數實現
:(){ :|:& };:
Bomb() { bomb|bomb $ }; bomb
腳本實現
#!/bin/bash
./$0|./$0&
示例:
十一、數組
數組是計算機編程語言上,是用於存儲多個相同類型數據的集合,把有限個類型相同的變量用一個名字命名,然後用編號區分它們的變量的集合,這個名字稱爲數組名,編號稱爲它們的下標,組成數組的各個變量爲數組的元素,數組是在程序設計中爲了處理方便,把具有相同類型的若干變量按有序的形式組織起來的一種形式,這些按序列排列的同類數據元素的集合稱爲數組,
1、數組的概念
變量:存儲單個元素的空間
數組:存儲多個元素的連續的內存空間,相當於多個變量的集合
2、數組的類型
索引數組:從0 開始,屬於數值索引,下標是數字
關聯數組:可支持使用自定義的格式,下標也可以是字符串
注意:數組的下標需放在中括號內
3、聲明數組
變量需先聲明在賦值使用
declare -x :聲明或顯示環境變量和函數
declare -g :設置函數爲全局函數
declare -xf :設置環境函數
declare -a 數組名 :聲明索引數組
declare -A 數組名: 聲明關聯數組
declare -a :查看所有索引數組
declare -A :查看所有關聯數組
注意:索引數組和關聯數組兩者不可相互轉換,聲明索引數組時,可以省略declare -a,直接賦值就可使用;聲明關聯數組時,必須使用declare -A聲明,否則無法使用
4、索引數組的賦值
1.挨個給數組的每個元素賦值
title[0]=1 : 數組名是title,下標是0,這個元素的值是1
title[1]=2 : 數組名是title,下標是1,這個元素的值是2
2.一次性給一個數組賦值多個元素,下標默認從0開始
title=(1 2 3 4 5) :數組名是title,默認是索引數組,所以下標從0開始,括號內的值分別賦值給title的每個元素,這種方法適用於元素的多個下標連續的情況下
3.指定數組下標給指定下標賦值
title=([0]=pig [1]=big [3]=bird [5]=fish) :當想要一次性給數組的多個元素賦值但是下標又不連續時
4.給數組的各個下標的值賦連續的值
title=({1..10}) :給數組10個元素賦10個連續的值
5.使用文件名給數組元素賦值
title=(/root*.sh) :文件名可以使用文件通配符
6.使用命令的結果賦值給數組
title=($(echo {1..10})) :把命令的結果賦值給元素時命令必須放在$()中
7.使用read輸入元素的值
read -a title :使用read命令手動輸入元素的值,以只能賦一個元的值,title是數組名,而且這個值只能賦值下標0這個元素
8.顯示已聲明的數組
declare -a :查看所有已聲明的數組
declare -a |grep 數組名 :查看某一已聲明的索引數組及其所有值
declare -A :查看所有已聲明的關聯數組
declare -A |grep 數組名 :查看某一已聲明的關聯數組及其所有值
5、引用數組
echo $title :不輸入下標,僅引用數組的下標爲0的值
echo ${title[n]} :引用指定下標的元素,下標爲n
echo ${title[*]} :顯示數組的所有值
echo ${title[@]} :顯示數組的所有值
echo ${#title[*]} :顯示數組總共有多少個元素
echo ${title[${#title[*]}-1]} :顯示數組的最後一個值(這個數組必須是索引數組,而且下標是連續的數值)
echo ${test[@]} :顯示數組的每一個值,以空格分隔
6、刪除數組
unset 數組名:刪除這個數組的所有值
unset 數組名[下標] :刪除這個數組的其中一個值,導致稀疏模式
7、數組切片
數組切片就是數組中有很多個值嘛,想要取出其中幾個連續的值。
示例:
8、想數組中追加元素
test[${#test[*]}]=cat :給索引數組的加值,加到最後一個索引的後面,(前提是這個數組的索引必須是連續無空缺的)
9、關聯數組的聲明與賦值
關聯數組賦值前必須先聲明,否則會默認爲時索引數組調用時會出錯
方法一:先聲明再賦值
declare -A 數組名 :聲明一個關聯數組,但是先不賦值
數組名[下標]=元素值 :給這個關聯數組其中一個元素賦值
Array_name=([idx_name1]=’val1’ [idx_name2]=’val2’...) :一次性給多個下標賦值,(array_name:數組名;idx_name:數組下標;val:元素值)
方法二:聲明並賦值
declare -A 數組名[下標]= 元素值 :聲明一個關聯數組,並給一個元素賦值
declare -A Array_name=([idx_name1]=’val1’ [idx_name2]=’val2’...) :聲明一個數組並同時給數組的多個元素賦值
示例:
10、小練習
聲明10 個隨機數保存於數組中,並找出其最大值和最小值
十二、字符串處理
1、字符串切片
${#var}:返回字符串變量var的長度
${var:offset}:返回字符串變量var中從第offset個字符後(不包括第offset個字符)的字符開始,到最後的部分,offset的取值在0到${#var}-1之間,(bash4.2後,允許負值)
${var:offset:number}:返回字符串變量var中從第offset個字符後 (不包括第offset個字符)的字符開始,長度爲number的部分
${var: -length}:取字符串的最右側幾個字符 (注意:冒號後必須有一空白字符)
${var:offset:-length}:從最左側跳過offset字符,一直向右取到 距離最右側lengh個字符之前的內容
${var: -length:-offset}:先從最右側向左取到length個字符開始 ,再向右取到距離最右側第offset個字符之間的內容(注意:-length前空格)
2、基於模式取子串
${var#*word}:其中word可以是指定的任意字符
功能:自左而右,查找var變量所存儲的字符串中,第一 次出現的word, 刪除字符串開頭至第一次出現word字符之間的 所有字符
${var##*word}:同上,貪婪模式,不同的是,刪除的 是字符串開頭至最後一次由word指定的字符之間的所有內容
${var%word*}:其中word可以是指定的任意字符;
功能:自右而左,查找var變量所存儲的字符串中,第一 次出現的word, 刪除字符串最後一個字符向左至第一次出現 word字符之間的所有字符;
${var%%word*}:同上,只不過刪除字符串最右側的字符向 左至最後一次出現word字符之間的所有字符;
3、查找替換
${var/pattern/substr}:查找var所表示的字符串中,第 一次被pattern所匹配到的字符串,以substr替換之
${var//pattern/substr}: 查找var所表示的字符串中, 所有能被pattern所匹配到的字符串,以substr替換之
${var/#pattern/substr}:查找var所表示的字符串中, 行首被pattern所匹配到的字符串,以substr替換之
${var/%pattern/substr}:查找var所表示的字符串中, 行尾被pattern所匹配到的字符串,以substr替換之
4、查找並刪除
${var/pattern}:刪除var所表示的字符串中第一次被 pattern所匹配到的字符串
${var//pattern}:刪除var所表示的字符串中所有被 pattern所匹配到的字符串
${var/#pattern}:刪除var所表示的字符串中所有以 pattern爲行首所匹配到的字符串
${var/%pattern}:刪除var所表示的字符串中所有以 pattern爲行尾所匹配到的字符串
5、字符大小寫轉換
${var^^}:把var中的所有小寫字母轉換爲大寫
${var,,}:把var中的所有大寫字母轉換爲小寫
十三、變量賦值
1、聲明變量
Shell變量一般是無類型的,但是bash shell提供了declare和typeset兩個命令用於指定變量的累心,兩個命令是等價的
變量可以先聲明變量名,然後再賦值,也可以聲明時在其後面同時賦值
使用declare或typeset命令聲明變量爲某一類型後
僅declare命令作用是查看所有函數
語法:declare [選項] 變量名[=value]
選項:
-r:聲明或顯示只讀變量(只讀變量聲明賦值後,值不可更改,unset命令也不可刪除)
-i:將變量定義爲整型數
-a:將變量定義爲索引數組
-A:將變量定義爲關聯數組
-f:顯示已定義的所有函數名及其內容
-F:僅顯示已定義的所有函數名
-x:聲明或顯示環境變量和函數
-l:聲明變量爲小寫字母 declare -l var=UPPER (使用該選項聲明變量後,無論給變量賦的值是大寫還是小寫字母都會被轉換爲小寫字母)
-u:聲明變量爲大寫字母 declare -l var lower (同上-l選項)
2、高級變量用法-有類型變量
${var:-value}或${var-value}:如果變量var爲空或爲設置,那麼返回value;否則返回var的值
${var:+value}:如果var非空,value,否則返回空值
${var:=value}:如果var爲空或未設置,那麼返回value,並將value賦值給var,否則返回var的值
${var:?value}:如果var爲空或未設置,那麼在當前終端打印value;否則返回var的值
爲腳本程序使用配置文件,實現變量賦值:
定義文本文件,每行定義“name=value”
在腳本中source此文件即可
3、eval命令
eval命令將會首先掃描命令行進行所有的置換,然後再執行該命令。該命令適用於那些一次掃描無法實現其功能的變量,該命令對變量進行兩次掃描
這個解釋不太容易理解,就是說,有一個變量的值是一條shell中的命令,那麼在調用這個變量時,並不是想調用變量本身的值,而是調用變量的值的那一條命令的結果,eval便可以實現這個功能,
比如給變量a賦值爲whoami這個命令:a=whoami;在這種情況下a的值就是whoami這串字符串,但是whoami又是shell中一個命令,我想要調用a的值的時候是這條命令的顯示結果,而不是這串字符串,就可以使用eval命令
但是eval只能轉換一層,不能多層轉換,比如,a=b;b=whoami,使用eval調用b的值是可以調出whoami的顯示結果,但是調用a時卻不能再一層調用爲whoami的結果
示例:
4、間接變量引用
間接變量引用:如果第一個變量的值是第二個變量的名字,從第一個變量引用第二個變量的值就稱爲間接變量引用
比如:var1的值是var2,而var2又是變量名,var2的值爲value,間接變量引用是指通過var1獲得變量值value的行爲
var1=var2
var2=value
Bash shell提供了三種格式實現間接變量引用
eval echo \$$var1
echo ${!var1}
eval echo ${!var1}
示例:
引用當前bash的進程號:echo $$
十四、創建臨時文件
在編輯腳本時,在腳本中有時還需要穿件一些其他文件供讀取使用,或者存放一些輸出的信息,創建文件時就可能出現文件名存在衝突,
或者一個程序同時被多個終端多個用戶訪問時,臨時文件名如果一樣的話,將會無法同時被編輯使用,所以臨時文件最好使用mktemp命令,創建隨機文件名的臨時文件,可避免衝突
mktemp:創建並顯示臨時文件;
僅mktemp命令創建的臨時文件保存在/tmp下以tmp開頭,後綴是10個字符的隨機字母或數字,
語法:mktemp [option] [template]
option:
-d:創建臨時目錄;(文件名也可以包括其絕對路徑)
-p DIR或--tmpdir=DIR:指明臨時文件所存放目錄位置,創建臨時文件
template:filename.XXX
X至少要出現3個,X代表的是一個隨機大小寫字母或數字,根據自己要創建的臨時文件名的長度確定多少個X
示例:
十五、安裝複製文件
Install命令是安裝或升級軟件或備份數據,它的使用權限是所有用戶。Install命令和cp米寧雷士,都可將文件目錄拷貝至指定的路徑下,但是install比cp命令更強大的一點就是它可以允許你控制目標文件的屬性,install通常用於程序的Makefile,使用它來講程序拷貝到目標(安裝)目錄
語法:install [option] [參數] source dest :將源文件source拷貝至目標目錄dest
option:
-d 或 --directory:將後面所有的參數都作爲目錄處理,然後創建指定目錄的所有主目錄
-D source dest:當目標文件目錄dest不存在時,會自動遞歸創建創建目標目錄,並源文件拷貝至目標文件
-g 組名 或 --group=GROUP:定義複製後的目標文件的所屬組
-o 用戶名 或 --owner=OWNER :定義複製後的目標文件的所屬主
-m 權限 或 --mode=MODE:定義複製後的目標文件的權限(默認755)
-p :以源文件的文件訪問、修改時間作爲相應的目標文件的時間屬性
-v :顯示覆制的過程
-t dest_dir source :將source文件件拷貝至dest_dir目錄下,dest_dir必須是個已存在的目錄,source可以是單個或多個文件文件,單不能是目錄,不能拷貝整個目錄下的所有文件
-T source dest;單文件傳輸,將文件source拷貝至dest,並將文件名更改爲目標文件名,source只能是一個文件,不能是目錄,dest是目標文件,不能是目錄,而且目標文件如果已存在,則會以source文件的內容覆蓋目標文件,如果目標文件不存在,則目標文件所在的目錄必須存在
注意:當install命令什麼選項都不帶的情況下,語法爲:install source dest;目標文件的默認權限爲755,更改時間是當前時間,如目標目錄不存在不會遞歸創建。
當源文件source是單個文件,dest是目錄時,將源文件複製到dest目錄下,dest不能是未存在目錄
當源文件source是單個文件,dest是已存在的文件時,則將source的內容覆蓋至dest文件中去
當源文件source是單個文件,dest是未存在的文件時,則dest文件的目錄必須存在,然後會在dest的目錄下創建一個與dest文件同名的文件,並將source的內容覆蓋進去
當源文件是多個文件時,dest必須是一個已存在的目錄
十六、Expect介紹
Expect是由Don Libes基於Tcl(Tool Command Language)語言開發的,主要應用於自動化交互式才做的場景,藉助expect處理交互式的命令,可以將交互式過程:ssh登錄,ftp登錄等寫在一個腳本上,使之自動化完成。尤其適用於需要對多臺服務器執行相同操作的環境中,可以大大提高系統管理人員的工作效率
要使用expect必須先安裝軟件纔可以使用,包名就是expect,這個包還依賴於tcl包
1、Expect語法
expect [選項] [-c cmds] [[-f|b] cmdfile] [args]
選項
-c :聰明航執行expect腳本,默認expect是交互地執行的
示例:expect -c ‘expect “\n” {send “pressed enter\n”}’
-d :不執行,僅輸出調試信息
示例:expect -d ssh.exp
2、expect 的五個命令
send:用於向進程發送字符串
expect:從晉城接收字符串
spawn:啓動新的進程
Interact:允許用戶交互
exp_continue:匹配多個字符串在執行動作後加此命令
1.Send
send命令接收一個字符串參數,並將該參數發送到進程
示例:send “hello there!\n” 輸出hello there
2.Expect
1)簡單expect
Expect命令和send命令正好相反,expect通常是用來等待一個進程的反饋。 Expect可以接收一個字符串參數,也可以接收正則表達式參數。和上文的send命令結合
示例:
expect “hi\n” {send “you said hi\n”} : 當在標準輸入中輸入hi\n的時候,輸出you said hi\n,(\n:換行符)
expect “hi\n” {send "you ";send “said\n”} : 當在標準輸入中輸入hi\n時,輸出you said;(大括號內可以有多個命令以分號隔開,當標準輸入或輸出中沒有空格符號時,可以不加雙引號)
2)模式-動作
Expect最常用的語法是來自tcl語言的模式-動作。這種語法及其靈活。
單一分支式語法:
expect “hi\n” {send “you said hi\n”} :匹配到hi時,會輸出you said hi
多分支模式語法:
expect “hi\n” {send “you said hi\n”} \
“hello” {send “hello yourself\n”} \
“bye” {send “good bye\n”}
匹配到hi、hello、bye任意一個字符時,執行相應的輸出,等同於如下寫法:
expect {
“hi\n” {send “you said hi\n”}
“hello” {send “hello yourself\n”}
“bye” {send “good bye\n”}
}
3.Spawn
Spawn:上文中的所有演示都是和標準輸入輸出進行交互,但是我們更希望他可以和某一個進程進行交互,Spawn命令就是用來啓動新的進程的,Spawn後send和expect命令都是和spawn打開的進程進行交互
示例:
注意:\r:\r的作用跟\n一樣,表示換行符
4.Insteract
到現在爲止,我們已經可結合spawn、expect、send自動化的完成很多任務了,但是,如何讓人在適當的時候干預這個過程了。比如下載完ftp文件時,扔可以停留在ftp命令行狀態,以便手動的執行後續命令。Interact可以達到這些目的,
示例:
5.exp_continue
在expect多分支模式下,雖有多個匹配的字符,但是匹配到哪一個分支的字符,才執行後面相應的動作,然後其他的都沒用了;如果想要匹配完上一個字符串執行過動作後繼續匹配下一個字符串執行動作,就在動作後加上exp_continue,可以繼續執行下面的匹配,相比較寫多個單分支模式來實現這個功能,語句簡便了許多
示例:
3、expect的使用方法
1.在命令行使用expect
直接輸入expect命令,進入expect模式,可以在這個模式下執行expect腳本,exit命令退出這個模式
示例:
使用-c選項,從命令行執行expect腳本,默認expect是交互地執行
示例:
2.在腳本中使用expect腳本
1)編寫expect腳本
編寫expect腳本時,其後綴名必須是.exp;開頭的沙邦必須爲#!/usr/bin/expect
示例:
2)在shell腳本中使用expect
在shell腳本中使用expect命令,腳本後綴名依然是.sh;開頭是#!/bin/bash;其他命令都正常使用,只有交互部分放在expect \<\<EOF與EOF之間
示例:
3)在shell腳本中調用執行已經編輯好的expect腳本
就是已經編輯好了的expect需要早另一個shell腳本中運行執行這個expect腳本
如需要執行expect腳本的絕對路徑爲/app/lx/login.exp;那麼就在shell腳本中編輯命令expect /app/lx/login.exp;在expect命令後跟expect腳本的絕對路徑即可,後面也可以跟參數,例如:expect /app/lx/login.exp a b c;
示例:調用login.exp進行批量管理,給多臺服務器創建用戶
第一步:編寫expect腳本
第二步:編輯存儲IP和用戶密碼的文件
第三步:編輯shell腳本,調用執行expect自動化腳本
第四步:執行shell腳本
4、在expect腳本中定義變量
1.定義變量
在expect腳本中定義變量與shell中不同,需使用set命令;語法格式如下:
set 變量名 值
例:set var abcd 給變量var賦值爲abcd,變量名和值之間沒有等於號
2.調用變量
調用變量跟shell腳本中一樣在變量名前加上$;例:$var
3.超時時間
超時時間timeout是一個固定的變量,它是用來設置超時時間的,就是在執行完這個expect腳本後,再過多少秒後退出這個腳本,timeout爲-1時爲永不超時,例如設置超時爲10秒,命令爲set timeout 10 ;單位是秒。
4.示例
5、Expect參數
Expect腳本可以接受從bash傳遞過來的參數,這些參數都存儲在數組argv中,可以使用[lindex $argv n]獲得,n從0開始,分別是第一個,第二個,第三個...參數;這種參數是位置參數
另外expect的命令行參數參考了c語言的,與bash shell有點不一樣,argc存儲了參數的個數;argv存儲了所有的參數,argv0存儲了腳本的名字,[lrange $argv 0 0]表示第一個參數,[lrange $argv 0 4]表示第一個參數到第五個參數
示例:
6、結束符
結束符expect eof 的作用是當執行到這一條命令時,超時時間爲多少秒,就停頓多少秒後再執行後續的expect語句,停頓期間任何命令也不能執行,用戶也不能交互式,輸入任何字符都沒有反應,就相當於shell腳本中sleep命令,停頓多少秒後繼續運行後續命令
退出expect腳本的命令依舊是exit,exit命令後續的所有命令將不再執行
示例:登錄成功後,因爲timeout是-1,永不超時,所以會一直停頓,
7、執行多個命令
當使用expect匹配一個字符串時,後面相對應的可以執行多個send語句的,這種情況下就是當連續的多個expect匹配的字符串相同,而後面的命令不同時,
示例: