做者:Danbo 日期:2015-7-3
什麼是Shell:Shell是一個命令解析器,它在OS的最外層,負責直接與用戶對話。
shell在系統四大層次所處的位置:(從內向外)
1.硬件
2.Kernel
3.Shell
4.外圍應用程序
什麼是Shell腳本:當命令或語句不在命令行執行,而是經過一個程序文件執行時,該程序就被稱爲Shell腳本或Shell程序。相似DOS下的批處理。經過命令、變量和流程控制語句等有機的結合起來,就造成了一個功能強大的Shell腳本。
示例1::清空日誌的三種方法:把全部命令堆積起來造成了腳本示例。
echo " " >/var/log/messages
>/var/log/messages
cat /dev/null >/var/log/messages
echo "Logs cleaned up"php
示例2:有判斷的shell的腳本
#!/bin/bash
ROOT_UID=0
LOG_DIR=/var/log
if [ "$UID" -ne "ROOT_UID" ]; then
echo "You must be root to run this script"
exit 1
fi
cd $LOG_DIR || {
echo "Cannot change to necessary directory." >&2
exit 1
}
cat /dev/null > messages
echo "Logs cleaned up."
exit 0 #注意返回0表示成功,返回1表示失敗。python
Shell腳本是弱類型語言,較爲通用的shell有標準的sh和csh,其中sh已被bash shell取代了。Shell相較於php、python、perl的差異:shell的優點在於吃力OS底層的業務(有大量的系統命令作支撐,2000多個命令,比grep、awk、sed )。好比一鍵安裝、報警腳本、shel開發快速。shell
規範的shell腳本開頭。
#!/bin/bash
#!又稱爲幻數,在執行bash及哦啊本的時候,內核會根據它來肯定哪一程序來解析腳本中的內容。這一行必須在腳本的頂端的第一行,好比不是第一行則是註釋。
咱們發現sh本質上是bash的軟連接:bash
[root@localhost ~]# which sh
/bin/sh
[root@localhost ~]# ll /bin/sh
lrwxrwxrwx 1 root root 4 Jun 29 17:03 /bin/sh -> bashssh
Shell腳本的執行
當Shell腳本以非交互的方式運行時,它會先檢查環境變量env,該便來個支出一個環境文件(.bashrc)後,從該變量環境變量文件開始執行,當讀取了env文件後,Shell纔開始執行Shell腳本的內容。學習
Shell腳本的執行有3中方式
①.bash script-name 或 sh script-name #當腳本自己沒有x權限或腳本開始沒有指定解析器。
②.path/script-name 或 ./script-name
③.source script-name 或 . script-name
使用source或者"."的話,能夠將子shell中的變量傳遞到父shell中
例如:測試
[root@localhost uestc]# echo 'userdir=`pwd`' >test.sh
[root@localhost uestc]# cat test.sh
userdir=`pwd`
[root@localhost uestc]# sh test.sh
[root@localhost uestc]# echo $userdir優化
[root@localhost uestc]# . ./test.sh
[root@localhost uestc]# echo $userdir
/uestcthis
這是爲何呢?咱們當前執行腳本的窗口是一個shell(經過echo $$來查看當前窗口的shell)。而test.sh有處於另一個shell中 。所以當咱們執行sh test.sh後,雖然tesh.sh中已經定義了userdir,可是沒法將其傳遞到父shell(當前窗口所處的shell)中來,而. /test.sh執行的方式就能夠。spa
變量可分爲環境變量(全局變量)和局部變量(本地變量)。
咱們設置環境變量在用戶家目錄的.bash_profile文件中,或者/etc/bashrc,或者/etc/profile,或者/etc/profile環境變量能夠在建立他們的shell和子shell,他們一般被稱爲全局變量以區別局部變量。一般環境變量應該大寫。環境變量可使用export內置命令或者declare -x導出爲全局變量。取消本地變量命令:unset 變量名
本地變量是在用戶當前的shell生存期的腳本中使用:定義方式爲:locate UESTC
定義變量的時候:"" '' 不加引號區別
不加引號:內容通常爲簡單連續的數字、字符串、路徑名等
單引號:輸出變量時引號裏面是什麼就輸出什麼
雙引號:雙引號中的變量會通過解析而後再輸出
注意再awk則相反:雙引號原樣輸出,單引號解析後再輸出
[root@localhost uestc]# UESTC=123456
[root@localhost uestc]# awk 'BEGIN{print "$UESTC"}'
$UESTC
[root@localhost uestc]# awk 'BEGIN{print '$UESTC'}'
123456
把命令定義爲變量
cmd=`date +%F`或者 cmd=$(date +%F)
當咱們對文件進行打包壓縮備份時爲顯示什麼時間備份的,咱們能夠將打包時間寫到文件名當中
tar -zcvf etc_$(date +%F)_backup.tar.gz /etc
Shell特殊變量
$0:得到當前執行Shell腳本的文件名,包括完整路路徑;只取名字:basename $0;只取路徑:dirname $0
[root@localhost uestc]# cat sh.sh
#!/bin/bash
echo $0
[root@localhost uestc]# cd scripts/
[root@localhost scripts]# sh /uestc/sh.sh
/uestc/sh.sh
$n:得到當前指定行的Shell腳本第n個參數值,n=1,2,3,4....9當大於10的時候就須要括起來$(10);
[root@localhost scripts]# cat 1.sh
#!/bin/bash
echo $1 $2 $3 $4 $5 $6 $7 $8 $9 $(10)
[root@localhost scripts]# sh 1.sh 1 2 3 4 //即取後面的形式
1 2 3 4
$*:得到當前Shell的全部參數,並將全部命令行參數視爲單個字符串
$#:得到當前Shell命令行中參數的總個數
$@:得到當前Shell的全部參數,每一個參數仍是獨立的。
咱們經過實例來看一下其做用
#!/bin/bash
case "$1" in
start)
start
;;
stop)
stop
;;
statue)
status portmap
;;
restart|reload)
restart
;;
condrestart)
[ -f /var/lock/subsys/portmap ] && restart || :
;;
*)
echo "Usage:$0{start|stop|status|restart|reload|condrestart}"
exit 1
esac
咱們這裏看一下$#的做用
[root@localhost scripts]# set -- "I am " an uestc student
[root@localhost scripts]# echo $#
4
咱們這裏看一下$*與$@的區別
[root@localhost scripts]# for i in "$@";do echo $i; done
I am
an
uestc
student
[root@localhost scripts]# for i in "$*";do echo $i; done
I am an uestc student
$$:獲取當前shell的進程號
$!:得到Shell最後運行後臺的PID
$?:或者執行上一個指令的返回值(0爲成功;非零爲失敗)
$_:在此以前執行命令或腳本的最後一個參數
[root@localhost ~]# cat ba.sh
#!/bin/bash
cd /etc
tar -zcvf service.tar.gz ./services >/dev/null 2>&1
[ $? -eq 0 ] && echo Success || echo Failed
咱們這裏總結一下$?返回值的意義:
0 成功
2 權限拒絕
126 找到命令可是沒法執行
127 未找打目標命令
大於128 命令被系統強制結束
另外一個例子:
#!/bin/bash
echo $$ >/uestc/a.log
while true
do
uptime > /dev/null 2>&1
sleep 2
done
此時咱們cat a.log此時咱們就能看到當前腳本的PID
[root@localhost ~]# sh ba1.sh &
[1] 13038
[root@localhost ~]# cat a.log
13038
Bash內部命令變量及shift實踐講解
有些內部命令在目錄列表是看不到的,能夠經過man bash來查看,常見的有:echo、eval、exec、 export、read、shift、exit和點(.)
shift語句按以下方式從新命令全部的位置參數變量,即$2成爲$1;$3成爲$2。在程序中每使用一次shift做用是全部的位置參數依次向左移動一個位置而且是位置參數$#減1,知道其值減到爲0
某些腳本加了一些選項再接參數。
eval(evalargs)讀入參數args並將他們組合成一個新的命令,而後執行。
好比:eval command-line
其中command-line是在終端上鍵入的一條普通命令行。然而當在它前面放上eval時,其結果是shell在執行命令行以前掃描它兩次。如:
pipe="|"
eval ls $pipe wc -l
shell第1次掃描命令行時,它替換出pipe的值|,接着eval使它再次掃描命令行,這時shell把|做爲管道符號了。
exec:當Shell執行到exec語句,不會去建立新的子進程,而是轉去執行指定的命令,當指定的明林執行完時,該進程(也就是最初的Shell)就終止了,因此Shell程序中exec後面的語句將再也不被執行。
Shell變量的字串經常使用操做
${#string} #返$string的長度 //echo ${string} | wc -m,可是這種技術比#string計數多一個
[root@localhost ~]# string=uestc
[root@localhost ~]# echo ${#string}
5
[root@localhost ~]# echo $string | wc -m
6
${string:position}:提取含有position關鍵字的字符串
${string:position:length}:從position以後開始提取長度爲length的字串
${string#substring}:從變量string開頭開始刪除最短匹配substring字串
${string##substring}:從變量string開頭開始刪除最長匹配substring字串
${string%substring}:從變量string結尾開始刪除最短匹配substring字串
${string%%substring}:從變量strig結尾開始刪除最長匹配substring字串
${string/substring/replace}:使用$replace,來代替第一個匹配$substring字串
${string/#substring/replace}:若是string前綴匹配substring,就用replace來替代/substring
好比咱們批量更名:
咱們首先將要建立的名字寫入到一個文件當中,好比:a.log
而後使用:for f in `cat a.log`; do touch $f ; done建立文件
咱們須要將每一個文件後面的finished去掉,該如何作?腳本以下:
#!/bin/bash
for f in `ls *.jpg`
do
mv $f `echo ${f%finished*}.jpg`
done
咱們將後綴jpg改成JPG,咱們先經過mv命令實現
#!/bin/bash
for f in `ls *.jpg`
do
mv $f `echo ${f/%jpg/JPG}`
done
經過sed命令也能夠實現:
#!/bin/bash
for f in `ls *.JPG`
do
mv $f `echo $f | sed 's/JPG/jpg/g'`
done
經過rename是最簡單的實現更名的方式:
rename "finished" '' * 第二種:rename .jpg .JPG *
其用法以下:
rename from to file
from:須要替換或須要處理的字符;
to:把前面from替換爲to;
file:代替換的文件,能夠用*處理全部文件。
${value:-word}:若是變量名存在且非空,則返回變量的值。不然,word字符串用途:字符變量未定義,則返回默認值。 範例:${vlaue:-word},若是value未定義,則表達式的值爲word。
${value:=word}若是變量名非空,則返回變量值。不然,設置這個變量值爲word,並返回氣值。用途,若是:若是變量未定義,則設置變量爲默認值,並返回默認值 。範例:${value:=word},若是value爲定義,則設置value值爲word,返回表達式的值也爲word。
${value:?message}:若是變量賦值的話正常替換,不然將消息message送到標準錯誤輸出。
實例:
path1="/uestc"
rm -rf ${path:-/tmp/}
這樣以後就比較保險了,即便目錄不存在會刪除tmp目錄內的臨時文件。
在腳本執行以前,咱們可使用sh -x來對腳本進行調試。
生產常見中咱們能夠看這兩個系統腳本:
/etc/init.d/httpd
/etc/init.d/crond
咱們再學習腳本的時候能夠多看一些系統腳本文件,能夠給系統腳本寫備註。
三種計算字符串長度的方法:
1.echo {#char}
2.echo ${char} | wc -c #這種計算方式會多計算一個換行字符"\n",比實際多1。
3.echo $(expr length "$char")
程序執行效率問題
咱們經過time+要執行的命令發現運用bash內置的命令進行執行操做時效率最高。
雙括號(())運算的示例
請你設計一個加、減、乘、除等功能的計算器。經過名列給你行的傳參的方式
#!/bin/bash
echo $(($1$2$3))
這個實際上是經過把後面的表達式傳遞到前面的腳本中執行,就是簡答的把3個參數排在一塊兒就OK了
Let變量的數值運算
[root@localhost ~]# i=2
[root@localhost ~]# let i=i+8
[root@localhost ~]# echo $i
10
[root@localhost ~]# i=1
[root@localhost ~]# i=i+1
[root@localhost ~]# echo $i
i+1
提示:let i=i+8 等同於((i+8)),可是後者的效率更高。
變量的數值運算與特殊應用expr命令
expr命令通常用於整數值,但也可用於字符串,用來求表達式變量的值,同時expr也是一個手工命令行計算器。
咱們以前用戶expr來計算字符串的長度:echo $(expr length "$char") //雙引號能夠不加
1.expr也能夠用在計算:
expr 2 + 2
expr 2 \* 2
注意運算符與數字以前都有空格。、
2.expr在循環中能夠用於增量計算。首先循環初始化爲0,而後循環值加1,反引號的用法爲命令替換。最基本的一種是從expr命令接受輸入並將之放入循環變量。
例如:給自變量i+1
[root@localhost ~]# i=0
[root@localhost ~]# i=`expr $i + 1`
[root@localhost ~]# echo $i
1
3.expr $[$a+$b]表達式形式,其中$a$b可爲整數值
expr $[2+3] #中括號數字與運算符以前能夠不加空格
5
以上這種書寫形式符號兩邊不須要加空格。expr將其後的串解釋爲表達式並計算其值,運算符前須要空格。
4.其餘特殊用法:此時咱們注意到ssh-cpoy-id腳本
if expr "$1" : ".*\.pub";then #匹配*.pub格式的文件若是是則爲真。例如:
expr "uestc.pub" : ".*\.pub"
9 #返回非0值爲真,若是返回值爲0則爲假。咱們注意到這個非0值實際上是返回前面字符個數。
5.爲了更方面閱讀咱們在紀委再加上兩個語句:expr "uestc.pub" : ".*\.pub" && echo MATCH || echo Not-MATCH
經過expr判斷變量是否爲整數。
#!/bin/bash
read -p "Please input an integer:" a
expr $a + 0 >&/dev/null
[ $? -eq 0 ] && echo INIT||echo Chars
這種判斷上輸入數字是否爲整數的一種重要的方式
6.經過expr計算字符串的長度
expr length "$UESTC"
bc命令的用法
1)、echo 3+4 | bc
7
2)、咱們要計算1+2+3+...+10也能夠經過管道接bc
seq -s "+" 10 |bc
55
3)、保留小數點問題:經過命令scale=n
[root@localhost ~]# echo "scale=3;4.35/2.11"|bc
2.061
4)、進制轉換obase=n
echo "obase=2; 8" | bc #這裏是將十進制的8轉化爲二進制。
[root@localhost ~]# echo "obase=2;8" | bc
1000
5)、typeset -i A=1 B=3
A=A+B
echo $A
4
不過這種方式不多見
6.$[]這種方式前面咱們使用expr講過了
[root@localhost ~]# echo $[1+2]
3
不過咱們仍是推薦使用$(())這樣的用法比較好
最經常使用的-p:prompt:設置提示信息
-t timeout設置輸入等待的時間,單位爲秒
read -t 10 -p "Please input two number:" a b #10s後不輸入退出
還有另一種方法:
echo -n "Please input two number:"
read a b
咱們寫個腳本對read讀入的是否是數字進行判斷
#!/bin/bash
while true
do
read -p "Please input two number:" a b
expr $a + 0 >/dev/null
[ $? -ne 0 ] && continue
expr $b + 0 >/dev/null
[ $? -ne 0 ] && continue || break
done
echo "a-b=$(($a-$b))"
echo "a+b=$[$a+$b]"
將上面改成命令行傳參的方式並優化
#!/bin/bash
a="$1"
b="$2"
Usage(){
echo "USAGE:sh $0 num1 num2"
exit 1
}
if [ $# -ne 2 ];then
Usage
fi
expr $1 + 0 >&/dev/null
[ $? -ne 0 ] && Usage
expr $2 + 0 >&/dev/null
[ $? -ne 0 ] && Usage
echo "a+b=$(($a+$b))"
echo "a+b=$[$a+$b]"
在bash的各類流程控制結構中一般要進行各類測試,而後根據測試結果執行不一樣的操做,優點也會經過與if等條件語句相結合,使咱們能夠方便的完成判斷。
格式1:test <測試表達式>
格式2:[ <測試表達式> ]
格式3:[[ <測試表達式> ]]
說明:格式1有格式2等價,格式3爲擴展test命令
經常使用的文件測試操做符號:
-f 文件存在
-d 目錄存在
-s 文件存在且非空
-e 文件存在即爲真
-r 文件存在且可讀
-w 文件存在且可寫
-x 文件存在且可執行
-L 文件存在且爲連接
f1 -nt f2 文件1比文件2更新
f1 -ot f2 文件1比文件2更久
咱們能夠經過查看/etc/init.d/nfs這個腳原本學習這些文件操做符的使用
test -f file && echo true||echo false
[ -f file ] && echo true|| echo false
[[ -f file && -d folder ]] && echo true||echo false
注意:&&或者||只能用在[]之間與[[]]以內。不過他們之間是能夠相互轉換的。
而且-o -a 能夠單[]中
字符串測試操做符
做用:比較兩個字符串是否相同,字符串長度是否爲零,是否爲null
-z "字符串" #若串長度爲0則真,-z能夠當即爲zero
-n "字符串" #若串的長度不會0則爲真
"串1"="串2" #相等爲真,此時也可使用符號==代替=
"串1"!="串2" #不相等爲真
注意:以上字符串測試操做符號必定要用""引發來!!!!
注意咱們看下面這個實例:
[root@localhost ~]# [ 2>1 ] && echo OK||echo False
False
[root@localhost ~]# [ 2<1 ] && echo OK||echo False
False
爲何?咱們必須注意當使用單中括號時必須使用轉義字符:
[root@localhost ~]# [ 2\<1 ] && echo OK||echo False
OK
因此說單括號咱們最好不要使用符號表示法:
[ 2 -lt 1 ] && echo OK||echo False
OK
因此咱們在[]中使用的比較 在(())和[[]]中
-eq ==
-ne !=
-gt >
-ge >=
-lt <
-le <=
邏輯操做符
在[]使用的邏輯操做符 在[[]]中使用的邏輯操做符
-a &&
-o ||
! !
咱們看一下一下示例
[ -f "$UESTC" ] $$echo 1||echo 0 #這個是條件表達式的用法:返回1爲真返回0爲假,這點狀態變量的$?不一樣
file1=/etc/services; file2=/etc/rc.local
echo $file1 $file2
[ -f "file1" ] && echo 1||echo 0
通常系統腳本中使用中會用到大量的判斷語句
[ -r /etc/s\sysconfig/network ] && . /etc/sysconfig/network
判斷條件後執行多條命令語句,也便是咱們剛開始經shell所用到的語句:
[ 判斷 ] || {命令1 命令2 命令3}
其用法實例:cd $LOG_DIR||{echo "Cannot change to necessary directory" >&2 exit 1}
對於上面的例子咱們能夠這樣寫:
[ 3 -ne 3 ] ||{
echo "I am a UESTC student"
echo "I am a GOOD man"
exit 1
}
若是要寫在一行中,每一個命令還須要分號結尾。
腳本中編寫實例:[ 3 -ne 3 ]||{echo "I am a UESTC Student";echo "LAAL";}
命令行中咱們能夠用:[ 3 -ne 3 ]||(echo s="1";echo "2")
還有一個例子:[ $ERROR -eq 0 ] && echo "jdk安裝成功" || (echo "jdk安裝失敗,請檢查" && exit 1)
咱們在nfs中找到相關的應用:
單分支結構
if [條件]; then
command
fi
雙分支結構