本文將結合前面介紹的Linux命令、管道符等知識,經過VI編輯器編寫Shell腳本,實現能自動化工做的腳本文件。linux
在講解Linux經常使用命令「linux入門系列5--新手必會的linux命令」一文中已提到,Shell終端解釋器是人機交互的橋樑,是用戶與內核之間的翻譯官。它做爲用戶與Linux系統內部通訊的媒介,爲用戶提供了一個面向Linux內核發送請求以便運行處橫向的界面系統級程序,用戶能夠經過Shell啓動、掛起、中止程序。shell
實際上Shell不只是一個命令行解釋器,除了能經過它控制程序的啓動、中止、掛起等操做外,它仍是一個強大的編程語言,易編寫、易調試、靈活性強。Shell是解釋執行的腳本語言,能夠調用Linux系統命令。express
Shell除了可以支持各類變量和參數外,還提供了諸如循環、分支等高級編程語言纔有的控制特性。要想正確使用這些特性,準確下達命令尤其重要,在講解具體編程語法以前,先學習一下Shell腳本的執行方式。編程
Shell腳本命令的工做方式有兩種:交互式和批處理。vim
經過SHELL環境變量,能夠查看當前系統默認使用的終端解釋器,默認採用Bash解釋器數組
[root@heimatengyun ~]# echo $SHELL
/bin/bash
複製代碼
在Shell腳本中不只會用到前文學習過的Linux命令、管道符、數據流重定向等語法規則,還須要把內部功能模塊化後經過邏輯語句進行處理,最終造成平常所見的shell腳本。bash
約定俗成的全部編程語言第一個程序都是Hello World,咱們用Shell編寫一個簡單的腳本編程語言
[root@heimatengyun test]# ls
test1.txt test2.txt
[root@heimatengyun test]# echo "hello world"
hello world
[root@heimatengyun test]# vi hello.sh
#!/bin/bash
#author:heimatengyun
echo " hello world"
複製代碼
保存並退出。Shell腳本文件能夠是任意名稱,可是爲了不被誤認爲是普通文件,建議後綴採用.sh,以表示是一個腳本文件。編輯器
ps:第一行#!/bin/bash是腳本聲明,用來告訴系統使用哪一種Shell解釋器來執行腳本。模塊化
第二行#author:heimatengyun是註釋信息,對腳本功能進行介紹方便往後維護。
第三行echo " hello world"是命令語句,能夠是簡單的各類linux命令,也能夠是經過各類邏輯語句和語法規則組合的複雜語句塊。
Shell編程格式就這麼簡單,我稱之爲Shell編程「三段式」,之後編碼就按照這個格式編寫便可。
Linux中執行腳本有兩種方式:用解釋器直接執行、經過腳本路徑執行。
語法:bash或sh 腳本名稱
案例:執行前文建立的hello.sh腳本文件
[root@heimatengyun test]# bash hello.sh
hello world
[root@heimatengyun test]# sh hello.sh
hello world
複製代碼
語法:腳本的絕對路徑或相對路徑
案例:執行前文建立的hello.sh腳本文件
[root@heimatengyun test]# /root/test/hello.sh
-bash: /root/test/hello.sh: Permission denied
[root@heimatengyun test]# ./hello.sh
-bash: ./hello.sh: Permission denied
複製代碼
納尼?提示權限不足。是的,你沒看錯,若是經過路徑這種方式運行,須要修改文件的執行權限,(默認的權限不能夠執行,而且在數據腳本路徑時按tab鍵也無補全提示)文件權限相關命令將在後續文章中繼續講解,此處先按照以下命令添加腳本的可執行權限便可。
[root@heimatengyun test]# ll
total 12
-rw-r--r--. 1 root root 4 Dec 1 09:48 hello.sh
[root@heimatengyun test]# chmod 777 hello.sh
[root@heimatengyun test]# ll
total 8
-rwxrwxrwx. 1 root root 53 Dec 1 09:22 hello.sh
複製代碼
再次執行,此時輸入路徑在按tab鍵也會提示自動補全了
[root@heimatengyun test]# ./hello.sh
hello world
[root@heimatengyun test]# /root/test/hello.sh
hello world
複製代碼
掌握Shell腳本運行方式後,咱們正式開始進入Shell編程語法學習。
任何一門語言的學習都沒有速成方法,都離不開大量的練習,只有多敲才能更加熟練,才能理解的更加深入。
Linux Shell(此處爲默認bash)中的變量分爲系統變量和用戶自定義變量,系統變量包括PWD、
USER等,用戶自定義變量則爲用戶根據實際須要自定義的變量。
能夠經過set命令查看Shell中全部變量。
[root@heimatengyun test]# set
BASH=/bin/bash
...省略部份內容
複製代碼
下文主要演示自定義變量
語法:
變量名=值
變量命名規則:
(1)變量名能夠由字母、數字和下劃線註冊,但不能以數字開頭
(2)等號兩側不能有空格
(3)變量名通常大寫
案例:
(1)定義變量並使用
[root@heimatengyun test]# SRT="wellcome"
[root@heimatengyun test]# echo $SRT
wellcome
[root@heimatengyun test]# set |grep SRT
SRT=wellcome
[root@heimatengyun test]# env |grep SRT
[root@heimatengyun test]#
複製代碼
能夠看到自定義變量SRT,能夠經過set命令查詢出來。
(2)變量提高爲全局環境變量
[root@heimatengyun test]# export SRT
[root@heimatengyun test]# env |grep SRT
SRT=wellcome
複製代碼
上例中自定義的變量SRT經過env命令查看,並未在環境變量中查詢出來,經過export將自定義變量SRT提高爲環境變量後,就能夠經過env查詢出來
(3)撤銷變量
[root@heimatengyun test]# unset SRT
[root@heimatengyun test]# echo $SRT
[root@heimatengyun test]# set |grep SRT
[root@heimatengyun test]# env |grep SRT
複製代碼
撤銷變量使用unset命令,撤銷以後變量將不存在
除了直接賦值,還能夠將命令執行的結果賦值給變量
語法:
變量=`命令` 或 變量=$(命令)
說明:
命令用反引號或$()包含起來,先執行命令而後將命令執行結果賦值給變量。
案例:
[root@heimatengyun test]# ls
hello.sh test1.txt test2.txt
[root@heimatengyun test]# RESULT=`ls`
[root@heimatengyun test]# echo $RESULT
hello.sh test1.txt test2.txt
複製代碼
位置參數變量主要用於取腳本的參數值,語法以下
變量名稱 | 功能 |
---|---|
$n | n爲數字,$0表示命令自己,![]() |
$* | 表示命令行中全部的參數,把參數當作一個總體 |
$@ | 表示命令行中全部的參數,把每一個參數區分對待 |
$# | 表示命令行中全部參數的個數 |
案例:
輸入2個參數,計算倆個數的和並打印輸出
[root@heimatengyun test]# vi sum.sh
#sum
#!/bin/bash
#分別接收2個參數
num1=$1
num2=$2
#求和
sum=$(($num1+$num2))
#打印
echo $sum
複製代碼
保存並退出,執行腳本輸入2個數查看執行結果
[root@heimatengyun test]# bash sum.sh 1 2
3
複製代碼
預約義變量都有特殊的做用,參看下表
變量名 | 功能 |
---|---|
$? | 表示***當前進程中***最後一條命令直接的返回狀態。0:執行成功;非0:執行失敗 |
$$ | 當前進程號(PID) |
$! | ***後臺運行***的最後一個進程的進程號(PID) |
案例:
(1)查看當前進程和後臺運行的最後一個進程
[root@heimatengyun test]# vi mypid.sh
#!/bin/bash
#輸出當前進程PID,也就是當前腳本運行時生成的PID
echo "當前進程PID=$$"
echo "最後一個後臺進程PID=$!"
複製代碼
保存退出,執行腳本
[root@heimatengyun test]# bash mypid.sh
當前進程PID=7810
最後一個後臺進程PID=
複製代碼
能夠看到$$和 !表示是後臺最有運行的一個進程的PID。
(2)輸出當前進程PID,並查看上一次命令執行結果
[root@heimatengyun test]# vi pid.sh
#!/bin/bash
#輸出當前進程PID,也就是當前腳本運行時生成的PID
echo "當前進程PID=$$"
#經過ls命令,查找不存在的文件,&表示讓命令後臺執行
ls -l XXX.txt&
echo "最後一個進程PID=$!"
echo "最後一條命令執行結果:$?"
複製代碼
保存退出,執行腳本
[root@heimatengyun test]# bash pid.sh
當前進程PID=7395
最後一個進程PID=7396
最後一條命令執行結果:0
[root@heimatengyun test]# ls: cannot access XXX.txt: No such file or directory
[root@heimatengyun test]#
複製代碼
能夠看到命令執行的進程和當前腳本運行的進程不是同一個,而且雖然xxx.txt文件不存在,但結果仍然返回爲0。若是改成查找一個已經存在的文件,毋庸置疑,返回結果確定仍然爲0。也就是說上邊腳本無論命令執行是否成功,都將返回0,緣由是經過&讓命令後臺執行,其實是新開了一個進程,而$?只能獲取到當前進程的最後一次執行命令結果,所以在作判斷命令是否執行成功是特別要當心。
語法:
(1)***[運算式]***
(2)expr m + n 注意expr運算符間要有空格
案例:
(1)採用$(())實現兩數相加
[root@heimatengyun test]# S=$((2+3))
[root@heimatengyun test]# echo $S
5
複製代碼
(2)採用$[]實現兩數相加
[root@heimatengyun test]# SUM=$[2+3]
[root@heimatengyun test]# echo $SUM
5
複製代碼
(3)採用expr命令實現兩數相加
[root@heimatengyun test]# S1=`expr 2 + 3`
[root@heimatengyun test]# echo $S1
5
複製代碼
注意expr命令時,操做符之間必定要有空格,不然執行的不是計算,而是字符串鏈接,以下示例演示了有空格和無空格的區別,另外乘法符號*因爲與通配符衝突,所以須要用\轉義
[root@heimatengyun test]# expr 2 + 3
5
[root@heimatengyun test]# expr 2+3
2+3
[root@heimatengyun test]# expr 2\*3
2*3
[root@heimatengyun test]# expr 2 \* 3
6
[root@heimatengyun test]# expr 2 * 3
expr: syntax error
複製代碼
(4)採用expr命令計算「2加3的和乘以5」
[root@heimatengyun test]# S2=`expr 2 + 3`
[root@heimatengyun test]# echo $S2
5
[root@heimatengyun test]# expr $S2 \* 5
25
複製代碼
注意操做符之間的空格,以上爲分步計算,也能夠直接一步計算,多層嵌套是注意須要轉義反引號,以下:
[root@heimatengyun test]# expr `expr 2 + 3` \* 5
25
[root@heimatengyun test]# echo `expr \`expr 2 + 3\` \* 5`
25
複製代碼
語法:[ 條件 ]
說明:條件先後必需要有空格,非空返回true,可使用$?驗證(0:true,非0:false)
案例:
(1)分別判斷存在和不存在的變量,檢驗返回值
[root@heimatengyun test]# [ $HOME ]
[root@heimatengyun test]# echo $?
0
[root@heimatengyun test]# [ $TEST ]
[root@heimatengyun test]# echo $?
1
複製代碼
因爲$HOME是環境變量確定存在,所以返回0,表示條件知足,變量不爲空;而TEST變量因爲沒定義,因此不存在,返回非0。
(2)條件判斷語句用於邏輯判斷
[root@heimatengyun test]# [ $HOME ]&& echo ok || echo notok
ok
[root@heimatengyun test]# [ $TEST ]&& echo ok || echo notok
notok
複製代碼
相似於其餘語言中的三元運算符,條件知足則執行緊隨其後的語句,不然不執行。
符號 | 含義 |
---|---|
= | 字符串比較 |
-lt | 小於 |
-le | 小於等於 |
-eq | 等於 |
-gt | 大於 |
-ge | 大於等於 |
-ne | 不等於 |
符號 | 含義 |
---|---|
-r | 有讀的權限 |
-w | 有寫的權限 |
-x | 有執行的權限 |
符號 | 含義 |
---|---|
-f | 文件存在且是一個常規文件 |
-d | 文件存在而且是一個目錄 |
-e | 文件存在 |
(1)整數比較
[root@heimatengyun test]# [ 1 -gt 2 ]
[root@heimatengyun test]# echo $?
1
複製代碼
1不大於2,因此輸出1,表示false
(2)文件類型判斷
[root@heimatengyun test]# [ -f test1.txt ]
[root@heimatengyun test]# echo $?
0
[root@heimatengyun test]# [ -f xxxx.txt ]
[root@heimatengyun test]# echo $?
1
複製代碼
test1.txt文件存在,輸出0表示true;而xxxx.txt文件不存在,因此輸出1表示false
(3)文件權限判斷
[root@heimatengyun test]# ll
-rw-r--r--. 1 root root 9 Nov 30 20:43 test1.txt
[root@heimatengyun test]# [ -x test1.txt ]
[root@heimatengyun test]# echo $?
1
複製代碼
因爲test1.txt 無執行權限,所以返回1,表示false
流程控制結構分爲:順序結構、分支結構、循環結構。順序結構根據語句依次順序執行,分支結構根據條件判斷運行不一樣的分支,循環結構則根據條件判斷是否循環執行。
分支語句分爲:if判斷語句、case語句
語法:
if [ 判斷條件 ]
then
程序
fi
或者then寫到判斷條件以後,用逗號隔開,等效於上邊語句
if [ 判斷條件 ];then
程序
fi
if判斷還能夠多層嵌套,形如:
if [ 判斷條件1 ]
then
程序1
elif [ 判斷條件2 ]
then
程序2
else
程序3
fi
說明:條件判斷語句中,注意***前中括號與if之間必須有空格,中括號先後也必須都有空格***
案例:
從鍵盤輸入年齡,根據年齡返回不一樣的形容語言
[root@heimatengyun test]# vim if.sh
#!/bin/bash
read -p "請輸入您的年齡:" AGE
if [ $AGE -le 18 ];then
echo "未成年"
elif [ $AGE -le 30 ];then
echo "年輕氣盛"
else
echo "糟老頭子"
fi
複製代碼
使用vim編輯器編輯內容(此處使用vim是由於vim比vi更適合在編程環境使用,有錯誤提示等功能,建議編寫腳本都採用vim),保存退出並執行腳本
[root@heimatengyun test]# bash if.sh
請輸入您的年齡:16
未成年
[root@heimatengyun test]# bash if.sh
請輸入您的年齡:40
糟老頭子
複製代碼
此處使用了read命令讀取鍵盤輸入,-p參數表示顯示提示符內容。
語法:
case $變量 in
" 值1")
語句1
;;
" 值2")
語句2
;;
*)
語句
;;
esac
案例:
根據傳入腳本的參數分別輸出一、二、3對應的英文
[root@heimatengyun test]#vim case.sh
#!/bin/bash
case $1 in
1)
echo one
;;
2)
echo two
;;
3)
echo three
;;
*)
echo "error"
;;
esac
複製代碼
保存退出並執行
[root@heimatengyun test]# bash case.sh 1
one
[root@heimatengyun test]# bash case.sh 2
two
[root@heimatengyun test]# bash case.sh 3
three
[root@heimatengyun test]# bash case.sh 8
error
複製代碼
循環語句分爲:for循環、while循環
for循環有兩種語法格式,分別以下:
語法1:
for 變量 in 值1 值2 值3...
do
程序
done
語法2:
for ((初始值;循環控制條件;變量變化))
do
程序
done
案例:
(1)一天三次問好
[root@heimatengyun test]#vim greeting.sh
#!/bin/bash
for time in morning afternoon evening
do
echo "good $time"
done
複製代碼
保存並退出,執行問候腳本,輸出問候語句
[root@heimatengyun test]# bash greeting.sh
good morning
good afternoon
good evening
複製代碼
(2)用for循環求1累加到5的和
[root@heimatengyun test]# vim getsum.sh
#!/bin/bash
sum=0
for ((i=0;i<=5;i++))
do
sum=$(($i+$sum))
done
echo "sum=$sum"
複製代碼
保存退出,並執行腳本
[root@heimatengyun test]# bash getsum.sh
sum=15
複製代碼
語法:
while [ 條件判斷式 ]
do
程序
done
案例:
(1)用while循環求1累加到5的和
[root@heimatengyun test]# vim while.sh
#!/bin/bash
i=1
sum=0
while [ $i -le 5 ]
do
sum=$[$i+$sum]
i=$[$i+1]
done
echo "sum=$sum"
複製代碼
保存並執行
[root@heimatengyun test]# bash while.sh
sum=15
複製代碼
注意:
一、是給變量賦值,左邊不用加
[
i=
i+1],不然報錯「command not found」
二、條件判斷語句注意左中括號與關鍵字之間、括號內部首位、操做符與操做數之間都有空格
三、i=
i+1],若是錯寫爲i=$i+1則會報錯「integer expression expected」。
函數是對功能的封裝,能夠提供代碼重用性。函數分爲系統函數和用戶自定義函數,對於系統函數直接拿來使用便可,自定義函數則是根據具體需求編寫。
使用現有的系統函數能夠提升工做效率,因爲篇幅所限,只簡單介紹兩個與文件路徑相關的系統函數,具體使用方法也能夠經過前面介紹的man命令來查詢函數具體的用法。
語法:
basename [文件路徑或字符串] [後綴]
功能描述:
刪掉全部的前綴,包括最後一個/,而後打印字符串;指定了後綴則在此基礎上再去掉後綴。
案例:
[root@heimatengyun test]# basename "sdf/sdf/sdf"
sdf
[root@heimatengyun test]# basename /root/test/test1.txt
test1.txt
[root@heimatengyun test]# basename /root/test/test1.txt txt
test1.
[root@heimatengyun test]# basename /root/test/test1.txt .txt
test1
複製代碼
語法:
dirname 文件絕對路徑
功能描述:
從給定的包含絕對路徑的文件名中去除文件名,而後返回生效的路徑。簡單說就是去掉非目錄部分,返回保留目錄部分,但不包含最後的/
案例:
[root@heimatengyun test]# dirname /root/test/test1.txt
/root/test
複製代碼
語法:
[function] 函數名[()]
{
語句;
[return 返回值;]
}
說明:
(1)shell是解釋執行而非編譯執行,所以語句是逐行執行的,必須在函數調用以前先聲明函數。
(2)函數返回值只能經過$?系統變量獲取。能夠顯示添加return語句返回(返回值範圍爲0-255),不然將以最後一條命令運行結果做爲返回值返回。
方括號內的部分function、參數、返回值能夠是可選的。function show() 和function show和show()這幾種形式都是能夠的。
案例:
(2)輸入1個整數,打印出比輸入小的整數
[root@heimatengyun test]# vim function.sh
#!/bin/bash
function printNumber()
{
i=0;
while [ $i -lt $1 ]
do
echo $i;
i=$[$i+1];
# i=`expr \`expr $i + 1 \``
sleep 1;
done
return 0;
}
read -p "請輸入一個數:" n;
printNumber $n;
複製代碼
保存文件並執行腳本
[root@heimatengyun test]# bash function.sh
請輸入一個數:5
0
1
2
3
4
複製代碼
經過上邊的演示,基本已經會編寫簡單的shell腳本了,本小節講解shell腳本接收用戶參數以及用戶參數的判斷。
以前提到過對linux命令來講,可否根據須要採用各類參數組合來完成特定的功能纔是衡量命令是否掌握的標準。一樣,函數或腳本也須要與用戶交互,能靈活處理用戶參數。
前面2.3中中已經提到幾個shell內設的用於接收參數的變量:*,
#
下面用案例進行演示
[root@heimatengyun test]# vim para.sh
#!/bin/bash
echo "當前腳本名稱:$0"
echo "總共有$#個參數,分別爲:$*"
echo "第一個參數爲:$1,第三個參數爲:$3"
複製代碼
保存腳本,並執行
[root@heimatengyun test]# bash para.sh 1 2 3
當前腳本名稱:para.sh
總共有3個參數,分別爲:1 2 3
第一個參數爲:1,第三個參數爲:3
複製代碼
能夠看到,腳本名稱後直接帶參數,參數之間用空格隔開,在腳本內部直接能夠獲取到各個位置上的參數
shell腳本中條件判斷語句能夠用來判斷表達式是否成立,若條件成立返回0表示true,不然返回其餘非0隨機數表示false。按判斷對象的不一樣,能夠分爲如下四種判斷語句:文件測試語句、邏輯測試語句、整數值比較語句、字符串比較語句。
文件測試便是用指定的條件判斷文件是否存在或權限是否知足,文件測試具體參數以下
運算符 | 做用 |
---|---|
-d | 測試文件是否爲目錄類型 |
-e | 測試文件是否存在 |
-f | 判斷是否爲通常文件 |
-r | 測試當前用戶是否有讀權限 |
-w | 測試當前用戶是否有寫權限 |
-x | 測試當前用戶是否有執行權限 |
案例:
判斷是否爲目錄類型的文件
[root@heimatengyun test]# [ -d /root/test ]
[root@heimatengyun test]# echo $?
0
[root@heimatengyun test]# [ -d /root/test/test1.txt ]
[root@heimatengyun test]# echo $?
1
複製代碼
先經過條件測試語句進行判斷,再使用shell解釋器內置的$?變量查看執行結果。因爲/root/test爲目錄,因此返回0;test1.txt是文件,因此返回1.
邏輯語句用於對測試結果進行邏輯分析,根據測試結果可實現不一樣的效果。邏輯運算符以下:
運算符 | 做用 |
---|---|
&& | 邏輯與,表示當前面的命令執行成功後纔會執行後邊的命令 |
|| | 邏輯或,表示當前面的命令執行失敗後纔會執行後邊的命令 |
! | 邏輯非,表示把條件測試中判斷結果取反。 |
案例:
判斷當前登陸用戶是否爲管理員
[root@heimatengyun test]# [ ! $USER = root ] && echo "user" || echo "root"
root
複製代碼
整數比較運算符僅是對數字的操做,不能將數字與字符串、文件等內容一塊兒操做,並且不能想固然地使用平常生活中的等號、大於號、小於號等來進行判斷。
ps:等號與賦值命令符衝突,大於小於號分別與輸出輸入重定向命令符衝突。所以必定要按照規範的整數比較運算符來進行操做。
可用的整數比較運算符以下表:
運算符 | 做用 |
---|---|
-eq | 是否等於 |
-ne | 是否不等於 |
-gt | 是否大於 |
-lt | 是否小於 |
-le | 是否小於或等於 |
-ge | 是否大於或等於 |
案例:
[root@heimatengyun test]# [ 1 -lt 2 ]
[root@heimatengyun test]# echo $?
0
[root@heimatengyun test]# [ 1 -gt 2 ]
[root@heimatengyun test]# echo $?
1
複製代碼
字符串比較語句用於判斷測試字符串是否爲空或兩個字符串是否相等。經常使用來判斷某個變量是否未被定義,即內容爲空值。字符串常見運算符以下表:
運算符 | 做用 |
---|---|
= | 比較字符串內容是否相同,相同爲0 |
!= | 比較字符串內容是否不一樣,不一樣爲0 |
-z | 判斷字符串內容是否爲空,空則爲0 |
案例:
分別查看存在和不存在的變量,區別返回值
[root@heimatengyun test]# echo $USER
root
[root@heimatengyun test]# echo $TEST
[root@heimatengyun test]# [ -z $USER ]
[root@heimatengyun test]# echo $?
1
[root@heimatengyun test]# [ -z $TEST ]
[root@heimatengyun test]# echo $?
0
複製代碼
至此,shell編程相關知識就介紹完畢,下一篇文章繼續學習與用戶和文件、權限相關的知識。