shell編程
1.什麼是shell?
Shell是一種編程語言, 它像其它編程語言如: C, Java, Python等一樣也有變量/函數/運算符/if語句/循環控制/… 但在開始之前, 我想先理清Shell語言與Shell之間的關係.
當命令不在命令行中執行,而是從一個文件中執行時,該文件就是shell腳本。
Shell是一種解釋型編程語言,不需要編譯,執行時也是按行執行。
Shell腳本是由解釋器解釋執行的,常見的解釋器有:bash dash ash ksh sh等
特點:
shell腳本是普通的文本文件,由流程控制邏輯和命令構成。
shell腳本通常以.sh作為後綴名,但不是必須的。
我們現階段學習的主要是bash dash.
2.shell腳本的編制、執行和調試
創建第一個shell腳本文件
#! /bin/sh
echo "hello shell!"
在shell編程中,通常情況下,#代表註釋,但是第一行的#是一個特例。
此時報錯是因為文件並沒有可執行權限,所以報了 Permission denied 錯誤。
報錯原因:
根據下面截圖我們也可以看出,HelloWorld.sh的文字顏色是白色,並不是綠色,所以也可以看出它並沒有執行權限。再者,當前我們使用的是root用戶,該用戶並沒有x權限,因此我們要賦權限, 可以使用 chmod 775 HelloWorld.sh命令或者chmod u+x HelloWorld.sh命令來賦權限。
其實運行shell程序共有三種方法,除了給文件賦予可執行權限外,還有另外兩種方法。
1. chmod +x使文件具有可執行權限, 直接運行
2. 直接調用解釋器, 將腳本文件作為參數傳入 (比如bash hi.sh)
3. 使用source(也可用 . 代替)執行文件
通常情況下, 最方便的方式就是方式1, 通過方式1執行你需要在腳本第一行寫好這段腳本由哪個解釋器來解釋, 而通過方式2來執行則沒有這個限制, 寫了也沒用. 除此之外方式1與方式2執行命令就沒有區別了, 但方式3執行的方式與前兩種都不同:
使用source執行shell腳本時, 不會創建子進程, 而是在父進程中直接執行!
3.變量 $
shell中沒有變量類型,本質上都是字符串
特點:
1. Shell中的變量只有字符串這一種類型
2. Shell中變量名與變量值沒有長度限制
3. Shell的變量也允許比較操作和整數操作, 只要變量中的字符串為數字
shell中有四種類型的變量
本地變量:僅在當前shell中有用,在shell外部或者子shell中都不可用
環境變量:當前及子shell有用
位置參數變量:腳本的參數或者函數的參數等
特殊變量:腳本內置的具有特殊用途的變量
位置參數變量
是一組特殊的內置變量跟在腳本名後面的用空格隔開的每個字符串
$1 表示第1個參數值,……,$9 表示第9個參數值
${10} 表示第10個參數值, ${11} 表示第11個參數值, ……
位置參數的用途
從 shell 命令/腳本 的命令行接受參數
在調用 shell 函數時為其傳遞參數
$#代表所有參數的總數。
特殊變量
命令行參數相關
$* 將所有位置參量看成一個字符串(以空格間隔) 。
$@ 將每個位置參量看成單獨的字符串(以空格間隔)。
"$*" 將所有位置參量看成一個字符串(以$IFS間隔)。
"$@" 將每個位置參量看成單獨的字符串(以空格間隔) 。
$0 命令行上輸入的Shell程序名。
$# 表示命令行上參數的個數。
進程狀態相關
$? 表示上一條命令執行後的返回值
$$ 當前進程的進程號
$! 顯示運行在後臺的最後一個作業的 PID
$_ 在此之前執行的命令或腳本的最後一個參數
4.流程控制和循環
if判斷
格式:
if condition
then
do something
elif condition
then
do something
elif condition
then
do something
else
do something
fi
if判斷中 if後用中括號[] 並且要特別注意空格, [ ] 兩邊一定要加空格, 結尾用fi
測試截圖如下:
其中, elif語句和else語句非必須的.看個例子:
For循環
基本結構:
for name [in list]
do
...
done
其中,[]括起來的 in list, 為可選部分, 如果省略in list則默認為in "$@", 即你執行此命令時傳入的參數列表.
舉例如下圖:
For循環腳本內容:
執行結果:
while循環
基本結構:
while condition
do
do something...
done
看個例子:
#! /bin/sh
i=0
while ((i<5));
do
((i++))
echo "i=$i"
done
輸出:
i=1
i=2
i=3
i=4
i=5
NOTE: 你可能需要去了解一下(())的用法
until循環
基本結構
until condition
do
do something...
done
看個例子:
#! /bin/sh
i=5
until ((i==0))
do
((i--))
echo "i=$i"
done
輸出:
i=4
i=3
i=2
i=1
i=0
跳出循環
shell中也支持break跳出循環, continue跳出本次循環.用法與C, Java中相同
5.退出/返回狀態
$?:返回上一條語句或腳本執行的狀態
0:成功
1-255:不成功
exit 命令
例如: exit n
exit 命令用於退出腳本或當前Shell
n 是一個從 0 到 255 的整數
0 表示成功退出,非零表示遇到某種失敗
返回值 被保存在狀態變量 $? 中
常見的返回狀態碼
0:
執行正確
1:
通用錯誤
126:
命令或腳本沒有執行權限
127:
命令沒找到
6.數據流重定向
標準輸入:代碼0,使用 < 或者 <<
標準輸出:代碼1,使用 > 或者 >>
錯誤輸出:代碼2,使用 > 或者 >>
> 表示 寫入; >>表示追加
舉例:
往hosts文件裡追加兩行域名的解析
創建updateHost.sh文件 編寫腳本語言,將特定內容自動寫入指定文件中。
執行腳本文件
查看(寫入成功)
7.命令的連續執行 && ||
腳本中經常有多條相關命令,比如:
執行備份日誌成功後刪除日誌
從服務器下載文件失敗後從備份服務器下載
&& 連起來的命令,後面的命令只有在前一命令執行成功後才執行
|| 連起來的命令,後面的命令只有在前一命令執行失敗後才執行
從服務器下載文件失敗後從備份服務器下載
#!/bin/sh
master_ip="172.172.172.172" #主服務器IP地址變量
bak_ip="172.172.172.182"#備份服務器IP地址變量
url="/downloads/?id=1800"#要下載的文件
curl -O "http://${master_ip}${url}" || curl -O "http://${bak_ip}${url}
命令的連接 | (管道符號)
8.awk——命令連接常用命令
簡介:
awk是一個非常棒的數據處理程序,它比較傾向於將一行分成多個'字段'來處理。
數據可以來自標準輸入、一個或者多個文件、其他進程的輸出
awk從第1行到最後一行逐行掃描,並執行匹配的操作
awk的默認操作是輸出匹配行即打印匹配行到標準輸出
awk語法:
awk 'BEGIN{語句1;語句2;...} END{語句1;語句2;...} 模式1{語句1;語句2;...} 模式2{語句1;語句2;...} 模式n...' [文件名]
如果沒有文件名,則數據來自標準輸入
BEGIN{...} 是awk開始運作前執行的語句;END{...}是awk最終結束後執行的語句,他們可以省略
awk默認分隔符是空格和tab,可以在BEGIN語句中為FS內置變量賦值重新定義分隔符
舉例:
awk 'BEGIN{print "awk start..."} END{print "awk end."} {print "awk programmer"}' /etc/profile
awk '{print $0}' /etc/profile #$0代表一整行數據,數據來自文件
echo "1 2 3 4" | awk '{print $1$4}' #$1代表第1列,$4代表第4列
echo "1 2 3 4" | awk '{print $1A$4}' #中間加入的字符A並不能輸出
echo "1 2 3 4" | awk '{print "$1A$4"}' #參數變量不能加入雙引號
echo "1 2 3 4" | awk '{print $1"A"$4}' #正確方式
echo "1 2 3 4" | awk '{print $1,"A",$4}' #注意與上式比較
注意單雙引號的使用!
適用情景:當我們只想得到某一指定位置的數據時,使用awk我們可以實現。
舉例1:
使用ps -ef | grep tomcat | grep -v grep | awk '{print $2}' 命令 可以查出並只顯示tomcat的進程號(注意單雙引號的使用):
案例:根據進程號Kill掉tomcat程序
執行該shell文件,則tomcat進程被殺死。
PS(shell中的時間格式轉換)
獲得當天的日期
date
獲取明天的日期
date -d next-day +%Y%m%d
獲取昨天的日期
date -d last-day +%Y%m%d
獲取上個月的年和月
date -d last-month +%Y%m
獲取下個月的年和月date -d next-month +%Y%m
獲取明年的年份date -d next-year +%Y
閱讀更多 HelloTeacher陳 的文章