NO.3 Shell腳本
程序在執行之前需要一個專門的編譯過程,把程序編譯成 為機器語言文件,運行時不需要重新翻譯,直接使用編譯的結果就行了。程序執行效率高,依賴編譯器,跨平臺性差些。如C、C++
解釋型語言:
程序不需要編譯,程序在運行時由解釋器翻譯成機器語言,每執 行一次都要翻譯一次。因此效率比較低。比如Python/JavaScript/ Perl /ruby/Shell等都是解釋型語言。
總結:
編譯型語言比解釋型語言速度較快,但是不如解釋性語言跨平臺性好。如果做底層開發或者大型應用程序或者操作系開發一般都用編譯型語言;如果是一些服務器腳本及一些輔助的接口,對速度要求不高、對各個平臺的兼容性有要求的話則一般都用解釋型語言。
回顧一下,Linux操作系統由什麽組成的?
內核、shell、應用程序、文件系統
shell:命令解釋器 人機交互的一個橋梁
終端——》命令
|
bash shell 解釋器(shell)
|
kernel
|
硬件
什麽是shell腳本?
簡單來說就是將需要執行的命令保存到文本中,按照順序執行它。它是解釋型的,意味著它不需要編譯。
若幹命令 + 腳本的基本格式 + 腳本特定語法 + 思想= shell腳本
什麽時候用到腳本?
重復化、復雜化的工作,通過把工作的命令寫成腳本,以後僅僅需要執行腳本就能完成這些工作。
如何學習腳本?
1、盡可能記憶更多的命令
2、掌握腳本的標準的格式(指定魔法字節、使用標準的執行方式運行腳本)
3、必須熟悉掌握腳本的基本語法(以下列表僅僅的基本要求,還有很多更深更難的語法需要自己擴充學習)
變量定義
條件判斷
分支語句
函數
數組
循環語句
正則表達式
sed,awk命令的使用
學習腳本的秘訣:
多看,多寫,多思考
看懂腳本——>模仿——>自己寫
腳本的編寫方法:
非標準:
source xxx.sh
. xxx.sh
bash xxx.sh
sh xx.sh
標準:
格式相對完整的腳本,建議的格式。
#!/bin/bash 腳本第一行 , #!魔法字符,指定腳本代碼執行的程序。即它告訴系統這個腳本需要什麽解釋器來執行,也就是使用哪一種Shell
Name: 名字
Desc:描述describe
Path:存放路徑
Usage:用法
Update:更新時間
。。。。
腳本執行方法:
標準腳本執行方法(建議):(魔法字節指定的程序會生效)
非標準的執行方法(不建議):(魔法字節指定的程序不會運作)
總結:
./xxx.sh --要求有執行權限,並且一定要聲明shell類型(#!/bin/bash)
. xxx.sh或者source xxx.sh或者bash xxx.sh或者sh xxx.sh --不需要有執行權限,也可以不聲明shell類型
說明: bash -x xxx.sh 或者sh -x xxx.sh --可以顯示執行過程,幫助排錯
補充:
bash中的引號:
雙引號 "" 會把引號的內容當成整體來看待,允許通過$符號引用其他變量值
單引號 ‘‘ 會把引號的內容當成整體來看待,禁止引用其他變量值,shell中特殊符號都被視為普通字符
反撇號 `` 和$() 反撇號和括號裏的命令會優先執行,如果存在嵌套,反撇號不能用。
; 可對一行命令進行分割,在執行過程中不考慮上一個命令執行是否是正確的
&& 邏輯與。可對一行命令進行分割,在執行過程中考慮上一個命令執行是否是正確的
|| 邏輯或
變量的分類:
本地變量:當前用戶自定義的變量。當前進程中有效,其他進程及當前進程的子進程無效。
unset a 取消變量
環境變量:當前進程有效,並且能夠被子進程調用。
HI=hello 設置一個本地變量
查看當前用戶的環境變量 env
env |grep HI
查詢當前用戶的所有變量<臨時變量與環境變量>
set |grep HI
HI=hello
export HI 將當前變量變成環境變量
env |grep -i HI
HISTSIZE=1000
HI=hello
全局變量:全局所有的用戶和程序都能調用,且繼承,新建的用戶也默認能調用。
/etc/profile
HI=Hello
export HI
/etc/profile
/etc/bashrc
~/.bash_profile
~/.bash_bashrc
user——>login——>/etc/profile——>~/.bash_profile——>/etc/bashrc——>~/.bashrc——>~/.bash_logout
局部變量:
~/.bash_profile
...
註意:需要重新登錄才生效
$HOME/.bashrc 當前用戶固定變量 eg:別名
$HOME/.bash_profile 當前用戶的環境變量
/etc/bashrc 使用bash shell用戶全局變量
/etc/profile 使用所有shell的全局變量
系統變量(內置bash中變量) : shell本身已經固定好了它的名字和作用。$@,$*,$# ,$$ ,$? ,$0
$#:腳本後面接的參數的個數
$*:腳本後面所有參數
$@: 腳本後面所有參數
$?:上一條命令執行後返回的狀態,當返回狀態值為0時表示執行正常,非0值表示執行異常或出錯
若退出狀態值為0 表示命令運行成功
若退出狀態值為127 command not found
若退出狀態值為126 找到了該命令但無法執行 ---權限不夠
若退出狀態值為1&2 沒有那個文件或目錄
$$ 當前所在進程的進程號
$! 後臺運行的最後一個進程號 (當前終端)
!$ 調用最後一條命令歷史中的參數
!! 調用最後一條命令歷史
$0 當前執行的進程/程序名
$1~$9 位置參數變量
${10}~${n} 擴展位置參數變量 第10個位置變量必須用{}大括號括起來
vim 3.sh
#!/bin/bash
#xxxxx
echo "\$0 = $0"
echo "\$# = $#"
echo "\$ = $"
echo "\$@ = $@"
echo "\$1 = $1"
echo "\$2 = $2"
echo "\$3 = $3"
echo "\$10 = ${10}"
什麽時候用到變量?
如果某個內容需要多次使用,並且在代碼中重復出現,那麽可以用變量代表該內容。這樣在修改內容的時候,僅僅需要修改變量的值
在代碼運作的過程中,可能會把某些命令的執行結果保存起來,後續代碼需要使用這些結果,就可以直接使用這個變量
變量定義的規則:
1、默認情況下,shell裏定義的變量是不分類型的,可以給變量賦與任何類型的值;等號兩邊不能有空格,對於有空格的字符串做為賦值時,要用引號引起來
955 A=hello
956 echo $A
957 A= hello
958 A =hello
959 A = hello
960 A=hello world
961 A=‘hello world‘
962 echo $A
963 A="hello world"
964 echo $A
2、變量的獲取方式: $變量名 ${變量名}
966 echo $A
967 echo ${A}
968 a=123456
969 echo $a
970 echo ${a}
971 echo ${a:2:3}
972 a=123456789
973 echo ${a:3:5} 3代表從第3位開始截取;5代表截取5個數
975 echo ${a:3} 代表截取從第3位開始以後所有的
3、取消變量的命令 unset 變量名
4、區分大小寫,同名稱但大小寫不同的變量名是不同的變量
5、變量名可以是字母或數字或下劃線,但是不能以數字開頭或者特殊字符
[root@node1 shell01]# a=1abc
[root@node1 shell01]# 1a=hello
bash: 1a=hello: command not found
[root@node1 shell01]# ?a=hello
bash: ?a=hello: command not found
[root@node1 shell01]# /a=hello
bash: /a=hello: No such file or directory
[root@node1 shell01]# _a=777
[root@node1 shell01]# echo $_a
777
6、命令的執行結果可以保存到變量
註意:
$( ) 等同於 執行符號 `,但是如果要嵌套使用,使用
符號就不行,要用$();但如果不是嵌套的使用
是可以的,如a="
which mount`which yum
"
7、數組
數組定義:用括號來表示數組,數組元素用“空格”符號分割開。定義數組的一般形式為:
array=(var1 var2 var3 var4)
或者
array[0]=v1
array[1]=v2
array[3]=v3
讀取數組:
${array [i]} i表示元素
使用@ 或 * 可以獲取數組中的所有元素:
hello,stu1
hello,stu2
hello,stu3
#!/bin/bash
array=(stu1 stu2 stu3)
for var in ${array[*]}
do
echo hello,$var
done
[root@node1 shell01]# var[0]=user1
[root@node1 shell01]# var[1]=user2
[root@node1 shell01]# var[2]=user3
[root@node1 shell01]# for i in ${var[@]};do echo hello,$i;done
hello,user1
hello,user2
hello,user3
獲取第n個元素
echo "${user[N-1]}"
獲取數組指定元素
echo ${user[@]:1:3} 從數組下標為1開始,讀取3個元素
示例:
定義一組用戶u01~u05,分別在屏幕上顯示hello,username
8、有類型變量
declare
-i 將變量看成整數
-r 使變量只讀 readonly
-x 標記變量通過環境導出 export
-a 將變量看成數組
[root@node1 shell01]# a=10
[root@node1 shell01]# b=2
[root@node1 shell01]# c=$a+$b
[root@node1 shell01]# echo $c
10+2
[root@node1 shell01]# declare -i a=5
[root@node1 shell01]# declare -i b=2
[root@node1 shell01]# declare -i c=$a+$b
[root@node1 shell01]# echo $c
7
[root@node1 shell01]#
[root@node1 shell01]# declare -i a=10 b=2
[root@node1 shell01]# declare -i c=$a*$b
[root@node1 shell01]# echo $c
20
1040 echo $a
1041 declare -r a=hello
1042 echo $a
1043 declare -r A=hello
1044 A=888
1045 echo $A
1046 declare -i A=123
1047 AB=hello
1048 export AB
1049 env|grep AB
1050 declare -x ABC=hahaha
1051 env|grep ABC
9、交互式定義變量的值 read 主要用於讓用戶去定義變量值
-p 提示信息
-n 字符數
-s 不顯示
-t 超時(默認單位秒) read -t 5 a
1054 read -p "Input your name:" name
1055 echo $name
1056 read -s -p "Input your password:" pass
1057 echo $pass
1058 read -n 5 -p "Input your name:" name
1059 echo $name
1060 read -t 3 -p "Input your name:" name
1061 echo $name
1062 read -t 3 -p "Input your name:" name
1063 echo $name
10、其他變量:
一個“#”代表從左往右去掉一個指定字符
兩個“#”代表從左往右最大去掉指定字符
一個“%”代表從右往左去掉一個指定字符
兩個“%”代表從右往左最大去掉指定字符
取出一個目錄下的目錄和文件
A=/root/Desktop/shell/mem.txt
echo $A
/root/Desktop/shell/mem.txt
dirname $A 取出目錄
/root/Desktop/shell
basename $A 取出文件
mem.txt
echo ${A%/} 從右往左去掉“/”內容
/root/Desktop/shell
echo ${A%%.} 從右往左最大長度去掉.後的內容
/root/Desktop/shell/mem
echo ${A%%.txt} 從右往左最大長度去掉.txt內容
/root/Desktop/shell/mem
echo ${A##//} 從左往右最大去掉所有"//"
mem.txt
echo ${A#/*/}
Desktop/shell/mem.txt
1071 aaa=/shell/shell01/dir1/file.txt
1072 echo $aaa
1073 dirname $aaa
1074 basename $aaa
1075 echo ${aaa#/*/}
1076 echo ${aaa##/*/}
1077 echo ${aaa%.*}
1078 echo ${aaa%%.*}
1079 echo ${aaa%/*/}
1080 echo ${aaa%/*}
1081 echo ${aaa%%/*}
===變量內容的替換===
[root@vm1 Desktop]# a=www.taobao.com
[root@vm1 Desktop]# echo ${a/taobao/baidu}
www.baidu.com
[root@vm1 Desktop]# a=www.taobao.com
[root@vm1 Desktop]# echo ${a/a/A}
www.tAobao.com
[root@vm1 Desktop]# echo ${a//a/A} 貪婪替換
www.tAobAo.com
===變量的替代===
echo ${var1-aaaaa}
aaaaa
var2=111
echo ${var2-bbbbb}
111
var3=
echo ${var3-ccccc}
${變量名:-新的變量值}
變量沒有被賦值(包括空值):都會使用“新的變量值“ 替代
變量有被賦值: 不會被替代
簡單的四則運算
算術運算:
默認情況下,shell就只能支持簡單的整數運算
-
-
- / %(取模,求余數)
-
$(()) | $[] | expr | let
Bash shell 的算術運算有四種方式:
1、使用 $(( ))
2、使用$[ ]
3、使用 expr 外部程式
4、使用let 命令
加法:
n=10
let n=n+1
或者let n+=1
echo $n
乘法:
let m=n*10
echo $m
除法:
let r=m/10
echo $r
求余數:
let r=m%7
echo $r
乘冪:
let r=m**2
echo $r
註意:
n=1
let n+=1 等價於let n=n+1
思考:能不能用shell做小數運算?
echo 1+1.5|bc
2.5
i++ 和 ++i (了解)
對變量的值的影響:
[root@vm1 Desktop]# i=1
[root@vm1 Desktop]# let i++
[root@vm1 Desktop]# echo $i
2
[root@vm1 Desktop]# j=1
[root@vm1 Desktop]# let ++j
[root@vm1 Desktop]# echo $j
2
對表達式的值的影響:
[root@vm1 Desktop]# unset i j
[root@vm1 Desktop]# i=1
[root@vm1 Desktop]# j=1
[root@vm1 Desktop]# let x=i++ 先賦值,再運算
[root@vm1 Desktop]# echo $i
2
[root@vm1 Desktop]# echo $x
1
[root@vm1 Desktop]# let y=++j 先運算,再賦值
[root@vm1 Desktop]# echo $j
2
[root@vm1 Desktop]# echo $y
2
條件判斷
語法結構:
if [ condition ];then
command
command
fi
if [ condition ];then
command1
else
command2
fi
if [ condition1 ];then
command1 結束
elif [ condition2 ];then
command2 結束
else
command3
fi
如果條件1滿足,執行命令1後結束;如果條件1不滿足,再看條件2,如果條件2滿足執行命令2;如果條件1和條件2都不滿足執行命令3.
if [ condition1 ];then
command1
if [ condition2 ];then
command2
fi
else
if [ condition3 ];then
command3
elif [ condition4 ];then
command4
else
command5
fi
fi
如果條件1滿足,執行命令1;如果條件2也滿足執行命令2,如果不滿足就只執行命令1結束;
如果條件1不滿足,不看條件2;直接看條件3,如果條件3滿足執行命令3;如果不滿足則看條件4,如果條件4滿足執行命令4;否則執行命令5
判斷語法:
1、test 條件表達式
2、[ 條件表達式 ]
3、[[ 條件表達式 ]] 匹配正則 =~
cat if3.sh
#!/bin/bash
aaa=$1
[[ $aaa = ‘hello‘ ]] && echo world
[root@node1 shell01]# bash -x if3.sh
-
aaa=
- ‘[‘ = hello ‘]‘
if3.sh: line 3: [: =: unary operator expected
[root@node1 shell01]# bash -x if3.sh hello
-
aaa=hello
-
‘[‘ hello = hello ‘]‘
- echo world
world
[root@node1 shell01]# vim if3.sh
[root@node1 shell01]# bash -x if3.sh
-
aaa=
- [[ ‘‘ = \h\e\l\l\o ]]
man test去查看,很多的參數都用來進行條件判斷
與文件存在與否的判斷
-e 是否存在 不管是文件還是目錄,只要存在,條件就成立
-f 是否為普通文件
-d 是否為目錄
-S socket
-p pipe
-c character
-b block
-L 軟link
文件權限相關的判斷
-r 當前用戶對其是否可讀
-w 當前用戶對其是否可寫
-x 當前用戶對其是否可執行
-u 是否有suid
-g 是否sgid
-k 是否有t位
-s 是否為空白文件 說明:-s表示非空,! -s 表示空文件
[ -s file1 ] file1文件內容不為空,條件成立
[ ! -s file1 ] file1文件內容為空,條件成立
兩個文件的比較判斷
file1 -nt file2 比較file1是否比file2新
file1 -ot file2 比較file1是否比file2舊
file1 -ef file2 比較是否為同一個文件,或者用於判斷硬連接,是否指向同一個inode
整數之間的判斷
-eq 相等
-ne 不等
-gt 大於
-lt 小於
-ge 大於等於
-le 小於等於
字符串之間的判斷
-z 是否為空字符串 字符串長度為0,就成立
-n 是否為非空字符串 只要字符串非空,就是成立
string1 = string2 是否相等
string1 != string2 不等
! 結果取反
多重條件判斷
邏輯判斷符號:
-a 和 && 邏輯與 [ 條件1 -a 條件2 ] 只有兩個條件都成立,整個大條件才成立
[ 1 -eq 1 ] && [ 2 -ne 3 ]
[ 1 -eq 1 -a 2 -ne 3 ]
-o 和 || 邏輯或 [ 條件1 -o 條件2 ] 只要其中一個條件成立,整個大條件就成立
[ 1 -eq 1 -o 2 -ne 2 ]
[ 1 -eq 1 ] || [ 2 -ne 2 ]
! 邏輯非 優先級最低
-a 優先級 比 -o 優先級要高
cat if4.sh
#!/bin/bash
aaa=id -u
[ $aaa -eq 0 ] && echo "當前是超級用戶" || echo "you不是超級用戶"
[ $(id -u) -eq 0 ] && echo "當前是超級用戶"
$ [ $UID -eq 0 ] && echo "當前是超級用戶" || echo "you不是超級用戶"
((1==2));echo $? C語言風格的數值比較
((1>=2));echo $?
demo1:
判斷一個IP是否通ping通
方法1:
#!/bin/bash
read -p "請輸入你要ping額IP地址:" ip
ping -c1 $ip &>/dev/null 或者 >/dev/null 2>&1
if [ $? -eq 0 ];then
echo "當前主機與所輸入的IP網絡ok"
else
echo "當前主機與所輸入的IP網絡不ok"
fi
方法2:
ping -c1 $1 &>/dev/null
test $? -ne 0 && echo "當前主機與所輸入的IP網絡不ok" || echo "當前主機與所輸入的IP網絡ok"
demo2:
判斷一個進程是否存在
1、ps -ef|grep vsftpd |grep -v grep
2、pidof 程序名稱
3、pgrep -l 程序名稱
#!/bin/bash
ps -ef|grep vsftpd|grep -v grep &>/dev/null
if [ $? -eq 0 ];then
echo "該進程存在"
else
echo "該進程不存在"
fi
read -p "請輸入你需要判斷的進程名字:" name
pidof $name &>/dev/null
[ $? -eq 0 ] && echo "該進程存在" || echo "該進程不存在"
pidof $1 &>/dev/null
test $? -ne 0 && echo "該進程不存在" || echo "該進程存在"
需求:使用該腳本判斷所輸入的進程是否存在(多個進程名,至少2個)
#!/bin/bash
[ $# -eq 0 ] && echo "該腳本$0的用法是:$0 pidname" || pidname=($*)
for i in ${pidname[@]}
do
pidof $i &>/dev/null
test $? -ne 0 && echo "該進程不存在" || echo "該進程存在"
done
pgrep命令:以名稱為依據從運行進程隊列中查找進程,並顯示查找到的進程id
選項
-o:僅顯示找到的最小(起始)進程號;
-n:僅顯示找到的最大(結束)進程號;
-l:顯示進程名稱;
-P:指定父進程號;pgrep -p 4764 查看父進程下的子進程id
-g:指定進程組;
-t:指定開啟進程的終端;
-u:指定進程的有效用戶ID。
demo3:
判斷一個服務是否正常(以httpd為例):
1、可以判斷進程是否存在,用/etc/init.d/httpd status判斷狀態等方法
2、最好的方法是直接去訪問一下,通過訪問成功和失敗的返回值來判斷
#!/bin/bash
wget -P /tmp http://localhost &>/dev/null
[ $? -eq 0 ] && echo "該服務正常" || echo "該服務不正常"
課堂練習:
1、寫一個腳本判斷一個用戶是否存在
2、完善上一個腳本的bug,要求當沒有給腳本傳參數或者參數個數不等於1個時,提示腳本的用法:usage:xxx.sh ip
#!/bin/bash
[ $# -ne 1 ] && echo "usage:basename $0
username" && exit
id $1 &>/dev/null
test $? -eq 0 && echo "該用戶$1存在" || echo "該用戶$1不存在"
#!/bin/bash
[ $# -ne 1 ] && echo "usage:basename $0
ipaddr" && exit
wget http://$1 &>/dev/null
[ $? -eq 0 ] && echo ‘httpd服務正常‘ || echo "httpd服務不正常"
3、判斷vsftpd軟件包是否安裝,如果沒有則自動安裝
4、判斷vsftpd服務是否啟動,如果啟動,輸出以下信息:
vsftpd服務器已啟動...
vsftpd監聽的地址是:
vsftpd監聽的端口是:
#!/bin/bash
ftp=vsftpd
rpm -q $ftp &>/dev/null
[ $? -ne 0 ] && yum -y install $ftp &>/dev/null
pgrep -l $ftp &>/dev/null
[ $? -ne 0 ] && service $ftp start &>/dev/null && echo "服務已經啟動..."
ip=netstat -nltp|grep $ftp|cut -d: -f1|cut -c21-
port=netstat -nltp|grep $ftp|cut -d: -f2|cut -d‘ ‘ -f1
echo "vsftpd監聽的地址是:$ip"
echo "vsftpd監聽的端口是:$port"
作業:
1、 判斷/tmp/run文件是否存在,如果不存在就建立,如果存在就刪除目錄裏所有文件
#!/bin/bash
dir=/tmp/run
[ -d $dir ] && rm -rf $dir/* || mkdir $dir
dir=/tmp/run
[ -f $dir ] && mv $dir $dir.bak
[ -d $dir ] && rm -rf $dir/* || mkdir $dir
2、 輸入一個路徑,判斷路徑是否存在,而且輸出是文件還是目錄,如果是字符連接,還得輸出是有效的連接還是無效的連接
#!/bin/bash
read -p "請輸入絕對路徑:" path
if [ -e "$path" -a -L "$path" ];then
echo "該文件是一個有效連接文件"
elif [ ! -e "$path" -a -L "$path" ];then
echo "該文件是一個無效連接文件"
elif [ -d $path ];then
echo "該文件是一個目錄"
elif [ -f $path ];then
echo "該文件是一個普通文件"
elif [ -e $path ];then
echo "其他文件"
else
echo "路徑不存在"
fi
3、交互模式要求輸入一個ip,然後腳本判斷這個IP 對應的主機是否 能ping 通,輸出結果類似於:
Server 10.1.1.20 is Down! 最後要求把結果郵件到本地管理員root@localhost和mail01@localhost
4、寫一個腳本/home/program,要求當給腳本輸入參數hello時,腳本返回world,給腳本輸入參數world時,腳本返回hello。而腳本沒有參數或者參數錯誤時,屏幕上輸出“usage:/home/program hello or world”
#!/bin/bash
if [ "$1" = "hello" ];then
echo world
elif [ "$1" = "world" ];then
echo hello
elif [ $# -ne 1 ];then
echo "usage:$0 hello or world"
else
echo "usage:$0 hello or world"
fi
5、寫一個腳本自動搭建nfs服務
#!/bin/bash
#1.安裝軟件
#2.確認軟件是否安裝
#3.配置
#(1).新建共享目錄,授本地權限
#(2).發布共享目錄/etc/exports
#4.啟動服務
#5.設置下次開機自動啟動
#配置網絡,測試網絡
ping -c 1 192.168.0.254 &> /dev/null && echo "##############網絡OK###############"
#配置network yum
rm -fr /etc/yum.repos.d/*
cat > /etc/yum.repos.d/dvd.repo << EOT
[base]
baseurl=ftp://192.168.0.254/rhel6_dvd
gpgcheck=0
EOT
#1.安裝軟件
yum -y install nfs* rpcbind &> /dev/null && echo "##############軟件安裝OK###############"
#2.xx
#3.配置
#(1).新建共享目錄,授本地權限
#read -p "請輸入你的共享目錄" dir
mkdir -p $1
chmod 777 $1 && echo "##############本地授權OK###############"
NO.3 Shell腳本