寫腳本是運維行業中最有趣的一部分(我是這麼覺得),一個功能強大的腳本可以省去許多的時間(如果以後都是自動化運維,我們該幹什麼呢?)。腳本的用途主要有:自動化處理、執行系統管理和故障排除、一鍵部署管理應用程序、批量處理文本文件。
shell是一個解釋型的腳本語言,它有一定的格式規範,bashshell是主流Linux的默認shell,在shell腳本的首行一般都有shebang機制,即首行會寫上使用哪一個shell解釋器,當給腳本賦予執行權限並執行時,Linux將根據這裏指定的shell去解釋執行。當然也可以直接將腳本文件當作參數給shell去執行,這時就不必要寫shebang了。
[root@localhost ~]# bash test.sh 1 [root@localhost ~]# ./test.sh 1
和其他編程語言的變量一樣,變量是指向一段內存空間的字符串。shell是弱類型的語言,使用變量前不必首先聲明,shell默認所有變量都是字符類型的。
變量命名:
良好的命名習慣可以方便團隊協作,能讓以後的工作人員在已有的腳本上進行擴展從而節省了運維成本
變量名只能用字母、數字和下劃線組成,數字不能作爲變量名的開頭
命名採用駝峯法以免出現歧義,要做到見名知意,變量名不能使用保留字
shell變量類型:
按照變量的內容可以分爲:字符型和數值型
字符型:
數值型:
按照變量的作用範圍分:
本地變量:變量只對當前shell生效
[root@localhost bin]# cat test1.sh #!/bin/bash var1="this is test1" bash /testdir/bin/test2.sh [root@localhost bin]# cat test2.sh #!/bin/bash echo $var1 [root@localhost bin]# bash test1.sh [root@localhost bin]#
可以看到執行test1.sh沒有任何輸出
環境變量:只對當前shell及其子shell有效,對父shell無效。
變量聲明、賦值
export name=value 或declare -x name=value,也可以聲明本地變量後用export和declare -x轉換爲環境變量.
引用方式和本地變量一樣
如果要顯示所有環境變量可用:export、env、printenv或declare -x
刪除環境變量:unset name
[root@localhost bin]# cat testenv.sh #!/bin/bash env1="this is a env test" name="this is a local variable" export env1 ./testenv2.sh [root@localhost bin]# cat testenv2.sh #!/bin/bash echo $env1 echo $name [root@localhost bin]# bash testenv.sh this is a env test [root@localhost bin]#
只讀變量:聲明後無法修改和刪除
變量聲明:readonly name或declare -r name,只讀變量在退出當前shell時失效
[root@localhost bin]# name="readonly" [root@localhost bin]# readonly name [root@localhost bin]# echo $name readonly [root@localhost bin]# name="hello" -bash: name: readonly variable [root@localhost bin]# unset name -bash: unset: name: cannot unset: readonly variable [root@localhost bin]# bash [root@localhost bin]# echo $name [root@localhost bin]# exit exit
只讀變量和本地變量一樣只能在當前shell生效,要想在子shell生效需要執行export變爲環境變量
位置變量:在腳本代碼中調用通過命令行傳遞給腳本的參數
$1、$2.........${10}.........:在腳本中調用傳遞給腳本的對應參數,注意:兩位數以上的數要加括號
$0表示腳本本身,如果執行腳本時寫的全路徑,$0就表示全路徑加腳本名
$*傳遞給腳本的所有參數,如果被引號括起來則是一個字符串
$@和$*一樣,但它叫所有參數看作不同的字符串
$#:傳遞給腳本的參數個數
$$:當前進程的pid
[root@localhost bin]# cat teshu.sh #!/bin/bash ./teshu1.sh "$*" echo "-------------------------" ./teshu1.sh "$@" echo "-------------------------" ./teshu1.sh $* echo "-------------------------" ./teshu1.sh $@ echo "-------------------------" [root@localhost bin]# cat teshu1.sh #!/bin/bash echo "the first arg is $1" echo "the first arg is $2" echo "the first arg is $3" echo "the num agrs is: $#" [root@localhost bin]# bash teshu.sh abc 123 hello the first arg is abc 123 hello the first arg is the first arg is the num agrs is: 1 ------------------------- the first arg is abc the first arg is 123 the first arg is hello the num agrs is: 3 ------------------------- the first arg is abc the first arg is 123 the first arg is hello the num agrs is: 3 ------------------------- the first arg is abc the first arg is 123 the first arg is hello the num agrs is: 3 ------------------------- [root@localhost bin]#
二、算數運算
bash中的算數運算有:+、-、*、/、%、**等
實現算數運算:
1、let var=算數表達式
[root@localhost bin]# let a=1+2 [root@localhost bin]# echo $a 3
2、var=$[算數表達式]
[root@localhost bin]# a=1 [root@localhost bin]# b=2 [root@localhost bin]# c=$[a+b] [root@localhost bin]# echo $c 3
3、var=$((算術表達式))
[root@localhost bin]# c=0 [root@localhost bin]# c=$((a+b)) [root@localhost bin]# echo $c 3
4、var=$(expr arg1 op arg2):算數操作符兩邊必須空格
[root@localhost bin]# a=2 [root@localhost bin]# b=2 [root@localhost bin]# c=`expr $a \* $b` [root@localhost bin]# echo $c 4 [root@localhost bin]# c=`expr $a * $b` expr: syntax error
5、echo "算是表達式" |bc 能夠支持小數運算
三、聚合命令
有兩種聚集命令的方式:
1、複合式
[root@localhost bin]# date;who |wc -l Sat Aug 13 11:07:13 CST 2016 2 [root@localhost bin]#
以分號爲分隔符,依次來執行命令,命令之間沒有邏輯關係
2、子shell式
[root@localhost bin]# (sleep 20 && ls /etc)|wc -l 270
當執行上面的命令時,括號生成一個新的bash,wc將等待子shell的結果
四、退出狀態碼
Linux中退出狀態碼來判斷進程是否執行成功,$?代表上一個進程的退出狀態碼,其中0代表成功,1-255代表失敗
自定義退出碼:exit
在腳本中有時我們通過判斷瞭解到當前環境不適合繼續執行該腳本,則可使用exit退出,默認退出碼是0
五、條件測試
判斷某需求是否滿足,需要由測試機制來實現
測試命令:
1、test expression一般很少用
2、[ expression ]中括號裏的expression兩邊必須加空格,一般由於數值判斷和文件判斷
3、` expression `expression兩邊必須加空格,一般用於字符串的判斷
數值測試:
-gt: 是否大於;
-ge: 是否大於等於;
-eq: 是否等於;
-ne: 是否不等於;
-lt: 是否小於;
-le: 是否小於等於;
數值測試比較大小不能用>、<等
字符串測試:
==:是否等於;
>:ascii碼是否大於ascii碼
<:是否小於
!=:是否不等於
=~:左側字符串是否能夠被右側的PATTERN所匹配,支持。注意:右側正則表達式爲擴展正則,但是不支持單詞錨定
-z:""STRING":字符串是否爲空,空爲真,不空爲假
-n :"STRING":字符串是否不空,不空爲真,空爲假
注意:用於字符串比較時的用到的操作數都應該使用引號
文件測試
存在性測試:
-a FILE:同-e
-e FILE: 文件存在性測試,存在爲真,否則爲假;
存在性及類別測試
-b FILE:是否存在且爲塊設備文件;
-c FILE:是否存在且爲字符設備文件;
-d FILE:是否存在且爲目錄文件;
-f FILE:是否存在且爲普通文件;
-h FILE 或-L FILE:存在且爲符號鏈接文件;
-p FILE:是否存在且爲命名管道文件;
-S FILE:是否存在且爲套接字文件;
注意:如果要判斷符號鏈接文件,應放在-f和-d的前面。
文件權限測試:
-r FILE:是否存在且可讀
-w FILE: 是否存在且可寫
-x FILE: 是否存在且可執行
注意:此處判斷的是當前登錄用戶的權限,且判斷時要注意該文件所在的目錄是否有執行權限
文件特殊權限測試:
-g FILE:是否存在且擁有sgid權限;
-u FILE:是否存在且擁有suid權限;
-k FILE:是否存在且擁有sticky權限;
文件大小測試:
-s FILE: 是否存在且非空;
文件是否打開:
-t fd: fd表示文件描述符是否已經打開且與某終端相關
-N FILE:文件自動上一次被讀取之後是否被修改過
-O FILE:當前有效用戶是否爲文件屬主
-G FILE:當前有效用戶是否爲文件屬組
雙目測試:
FILE1 -ef FILE2: FILE1與FILE2是否指向同一個設備上的相同inode
FILE1 -nt FILE2: FILE1是否新於FILE2;
FILE1 -ot FILE2: FILE1是否舊於FILE2;
六、接受用戶輸出:
使用read來把輸入值分配給一個或多個shell變量:
-p:指定要顯示的提示
-t:timeout指定等待超時時間
read 從標準輸入中讀取值,給每個單詞分配一個變量所有剩餘單詞都被分配給最後一個變量
七、練習:
1、編寫腳本/root/bin/systeminfo.sh,顯示當前主機系統信息,包括主機名,IPv4地址,操作系統版本,內核版本,CPU型號,內存大小,硬盤大小。
[root@localhost bin]# bash systeminfo.sh Hostname: localhost.localdomain IP address: 10.1.70.102 System version: CentOS Linux release 7.2.1511 (Core) Kernel release: 3.10.0-327.el7.x86_64 CPU type: Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz Memory size: 1.8G Disk size: 200G [root@localhost bin]# cat systeminfo.sh #!/bin/bash hostname=`hostname` ipaddr=`ifconfig |sed -nr '2s/^[^[:digit:]]+([0-9.]+\b).*/\1/p'` system=`cat /etc/centos-release` kernel=`uname -r` cpu=`grep "model name" /proc/cpuinfo |head -1|cut -c14-54` mem=`free -h|grep "Mem"|tr -s ' '|cut -d ' ' -f2` disk=`lsblk|grep -w "sda"|tr -s ' '|cut -d ' ' -f4` echo -e "Hostname: \033[33m$hostname\033[0m" echo -e "IP address: \033[33m$ipaddr\033[0m" echo -e "System version: \033[33m$system\033[0m" echo -e "Kernel release: \033[33m$kernel\033[0m" echo -e "CPU type: \033[33m$cpu\033[0m" echo -e "Memory size: \033[33m$mem\033[0m" echo -e "Disk size: \033[33m$disk\033[0m" unset hostname ipaddr system kernel cpu mem disk
2、編寫腳本/root/bin/backup.sh,可實現每日將/etc/目錄備份到/root/etcYYYY-mm-dd中
[root@localhost bin]# bash backup.sh copying [root@localhost bin]# echo $? 0 [root@localhost bin]# bash backup.sh copying has been done today [root@localhost bin]# echo $? 1
[root@localhost bin]# cat backup.sh #!/bin/bash date=`date +%F` [ -e /root/etc${date} ] && echo "copying has been done today" && exit 1 || echo "copying" && cp -r /etc /root/etc${date}
3、編寫腳本/root/bin/disk.sh,顯示當前硬盤分區中空間利用率最大的值
[root@localhost bin]# bash disk.sh The maximum value of disk utilization is:38% [root@localhost bin]# cat disk.sh #!/bin/bash rate=`df|tr -s ' '|grep -oE "\b[0-9]{1,3}%"|sort -nr|head -1` echo "The maximum value of disk utilization is:$rate" unset rate
4、編寫腳本/root/bin/links.sh,顯示正連接本主機的每個遠程主機的IPv4地址和連接數,並按連接數從大到小排序
[root@localhost bin]# bash links.sh the link num is: 2 10.1.250.91 [root@localhost bin]# cat links.sh #!/bin/bash echo -e "the link num is:\n `netstat -nt|tr -s ' '|cut -d ' ' -f5|grep -o "^[0-9.]\+\b"|sort|uniq -c|sort -nr`"
5、寫一個腳本/root/bin/sumid.sh,計算/etc/passwd文件中的第10個用戶和第20用戶的ID之和
[root@localhost bin]# bash sumid.sh the sum of uid is: 184 [root@localhost bin]# cat sumid.sh #!/bin/bash ten=`sed -n '10p' /etc/passwd|cut -d: -f3` twe=`sed -n '20p' /etc/passwd|cut -d: -f3` echo "the sum of uid is: $((twe+ten))" unset ten twe
6、寫一個腳本/root/bin/sumspace.sh,傳遞兩個文件路徑作爲參數給腳本,計算這兩個文件中所有空白行之和
[root@localhost bin]# bash sumspace.sh /etc/profile /etc/issue /usr/bin/echo The /etc/profile has spaceline: 11 The /etc/issue has spaceline: 1 The /usr/bin/echo has spaceline: 1 The total spaceline is:13 [root@localhost bin]# cat sumspace.sh #!/bin/bash count=0 for i in `seq 1 $#` do if [ ! -f $1 ];then echo "Please type right name of file $1" else num=`grep "^[[:space:]]*$" $1|wc -l` echo "The $1 has spaceline: $num" let count=$count+$num fi shift done echo "The total spaceline is:$count" unset count num
7、寫一個腳本/root/bin/sumfile.sh,統計/etc, /var, /usr目錄中共有多少個一級子目錄和文件
[root@localhost bin]# bash sumfile.sh /etc/ /var/ /usr/ the /etc/ has file 270 the /var/ has file 21 the /usr/ has file 12 Total num of file is 303 [root@localhost bin]# bash sumfile.sh Total num of file is 0 [root@localhost bin]# bash sumfile.sh /etc please enter dir name like /etc/ Total num of file is 0 [root@localhost bin]# bash sumfile.sh /etc/ /var/ /usr/ the /etc/ has file 270 ^[[3~the /var/ has file 21 the /usr/ has file 12 Total num of file is 303 [root@localhost bin]# bash sumfile.sh /etc/ /var/ /usr/ /var/ /bin/ /usr/bin/ the /etc/ has file 270 the /var/ has file 21 the /usr/ has file 12 the /var/ has file 21 the /bin/ has file 1923 the /usr/bin/ has file 1923 Total num of file is 4170 [root@localhost bin]# cat sumfile.sh #!/bin/bash count=0 for i in `seq 1 $#` do if [[ ! $1 =~ .*/$ ]];then echo "please enter dir name like /etc/" elif [ ! -d $1 ];then echo "$1 does not exsit" else dir=`echo "$1"|sed -r 's@.*@&*@'` num=`ls -d $dir|wc -l` echo "the $1 has file $num" let count=$count+$num fi shift done echo "Total num of file is $count" unset count dir num [root@localhost bin]#
8、寫一個腳本/root/bin/argsnum.sh,接受一個文件路徑作爲參數;如果參數個數小於1,則提示用戶“至少應該給一個參數”,並立即退出;如果參數個數不小於1,則顯示第一個參數所指向的文件中的空白行數
[root@localhost bin]# bash argsnum.sh Please type filename:/etc/profile The firstfile has spaceline 11 [root@localhost bin]# bash argsnum.sh Please type filename:/etc/issue The firstfile has spaceline 1 [root@localhost bin]# bash argsnum.sh Please type filename: One file should be input at least [root@localhost bin]# bash argsnum.sh Please type filename:/etc The firstarg is not a file [root@localhost bin]# cat argsnum.sh #!/bin/bash read -p "Please type filename:" file if [[ -z $file ]];then echo "One file should be input at least" exit 1 else firstfile=`echo $file|cut -d ' ' -f1` if [[ ! -f $firstfile ]];then echo "The firstarg is not a file" exit 2 else num=`grep -E "^[[:space:]]*$" $firstfile|wc -l` echo "The firstfile has spaceline $num" fi fi unset file firstfile num
9、寫一個腳本/root/bin/hostping.sh,接受一個主機的IPv4地址做爲參數,測試是否可連通。如果能ping通,則提示用戶“該IP地址可訪問”;如果不可ping通,則提示用戶“該IP地址不可訪問”
[root@localhost bin]# bash hostping.sh Please type ipaddr:10.1.0l.1 ping failture [root@localhost bin]# bash hostping.sh Please type ipaddr:10.0.0.1 ping failture [root@localhost bin]# bash hostping.sh Please type ipaddr:10.1.0.1 ping successfull [root@localhost bin]# cat hostping.sh #!/bin/bash read -p "Please type ipaddr:" ip ping -c1 -W1 $ip &> /dev/null && echo ping successfull || echo ping failture
10、判斷硬盤的每個分區空間和inode的利用率是否大於80,如果是,發郵件通知root磁盤滿
[root@localhost bin]# cat diskwarnning.sh #!/bin/bash while true;do rate=`(df ;df -i )|tr -s ' '|cut -d ' ' -f5|grep -oE "\b[0-9]{1,3}\b"|sort -nr|head -1` if [ $rate -gt 80 ];then echo "The disk will be filled"|mail -s warnning root && break else echo "The disk is enough to use,maximum utilization is ${rate}%" fi sleep 1 done unset rate while true;do echo "Mail has been sent,please to check" sleep 1 done
[root@localhost bin]# bash diskwarnning.sh The disk is enough to use,maximum utilization is 38% The disk is enough to use,maximum utilization is 38% The disk is enough to use,maximum utilization is 38% The disk is enough to use,maximum utilization is 38% The disk is enough to use,maximum utilization is 38% .........
[root@localhost testdir]# dd if=/dev/zero of=zero bs=1G count=17
可以看到,磁盤最大利用率一直在增大,當大於80%時就發送報警郵件
[root@localhost bin]# mail Heirloom Mail version 12.5 7/5/10. Type ? for help. "/var/spool/mail/root": 5 messages 5 new >N 1 root Tue Aug 16 09:13 18/626 "warnning" N 2 root Tue Aug 16 09:13 18/626 "warnning"
[root@localhost testdir]# df Filesystem 1K-blocks Used Available Use% Mounted on /dev/sda2 51175000 4320900 46854100 9% / devtmpfs 919820 0 919820 0% /dev tmpfs 934344 0 934344 0% /dev/shm tmpfs 934344 9112 925232 1% /run tmpfs 934344 0 934344 0% /sys/fs/cgroup /dev/sda5 20469760 17858788 2610972 88% /testdir /dev/sda1 406188 151904 254284 38% /boot tmpfs 186872 0 186872 0% /run/user/0 You have mail in /var/spool/mail/root
11、指定文件做爲參數,判斷文件是否爲.sh後綴,如果是,添加x權限
[root@localhost bin]# ll total 68 -rw-r--r--. 1 root root 24 Aug 13 18:07 a -rw-r--r--. 1 root root 372 Aug 13 17:12 argsnum.sh -rw-r--r--. 1 root root 151 Aug 13 15:41 backup.sh -rw-r--r--. 1 root root 140 Aug 13 15:55 disk.sh -rw-r--r--. 1 root root 377 Aug 16 09:15 diskwarnning.sh -rw-r--r--. 1 root root 123 Aug 13 17:17 hostping.sh -rwxr-xr-x. 1 root root 0 Aug 13 11:12 juji.sh -rwxr-xr-x. 1 root root 124 Aug 13 16:02 links.sh -rw-r--r--. 1 root root 262 Aug 16 09:36 privilege.sh -rw-r--r--. 1 root root 355 Aug 13 16:42 sumfile.sh -rw-r--r--. 1 root root 151 Aug 13 16:07 sumid.sh -rwxr-xr-x. 1 root root 281 Aug 13 16:23 sumspace.sh -rwxr-xr-x. 1 root root 671 Aug 13 15:33 systeminfo.sh -rwxr-xr-x. 1 root root 120 Aug 13 10:51 teshu1.sh -rwxr-xr-x. 1 root root 209 Aug 13 10:50 teshu.sh -rwxr-xr-x. 1 root root 34 Aug 13 10:06 testenv2.sh -rw-r--r--. 1 root root 96 Aug 13 10:07 testenv.sh -rwxr-xr-x. 1 root root 1 Aug 13 18:25 warnning.sh [root@localhost bin]# bash privilege.sh a argsnum.sh backup.sh disk.sh diskwarnning.sh hostping.sh privilege.sh sumfile.sh sumid.sh /etc/init.d/functions /var/log/secure The file a is not a script file! Execute permission of argsnum.sh has been added Execute permission of backup.sh has been added Execute permission of disk.sh has been added Execute permission of diskwarnning.sh has been added Execute permission of hostping.sh has been added Execute permission of privilege.sh has been added Execute permission of sumfile.sh has been added Execute permission of sumid.sh has been added The file /etc/init.d/functions is not a script file! The file /var/log/secure is not a script file! [root@localhost bin]# ll a argsnum.sh backup.sh disk.sh diskwarnning.sh hostping.sh privilege.sh sumfile.sh sumid.sh /etc/init.d/functions /var/log/secure -rw-r--r--. 1 root root 24 Aug 13 18:07 a -rwxr-xr-x. 1 root root 372 Aug 13 17:12 argsnum.sh -rwxr-xr-x. 1 root root 151 Aug 13 15:41 backup.sh -rwxr-xr-x. 1 root root 140 Aug 13 15:55 disk.sh -rwxr-xr-x. 1 root root 377 Aug 16 09:15 diskwarnning.sh -rw-r--r--. 1 root root 13948 Sep 16 2015 /etc/init.d/functions -rwxr-xr-x. 1 root root 123 Aug 13 17:17 hostping.sh -rwxr-xr-x. 1 root root 262 Aug 16 09:36 privilege.sh -rwxr-xr-x. 1 root root 355 Aug 13 16:42 sumfile.sh -rwxr-xr-x. 1 root root 151 Aug 13 16:07 sumid.sh -rw-------. 1 root root 711 Aug 16 08:51 /var/log/secure [root@localhost bin]#
12、判斷輸入的IP是否爲合法IP
[root@localhost bin]# cat ipcheck.sh #!/bin/bash read -p "Please enter the ipaddr:" ip echo $ip | grep -E "^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\>$" &> /dev/null && echo "this is a useful ip" || echo "this is not useful ip" [root@localhost bin]# bash ipcheck.sh Please enter the ipaddr:10.10.10.10 this is a useful ip [root@localhost bin]# bash ipcheck.sh Please enter the ipaddr:10.10.10.10. this is not useful ip [root@localhost bin]# bash ipcheck.sh Please enter the ipaddr:10.10.1.256 this is not useful ip
13、計算1+2+3+...+100
[root@localhost bin]# bash sumand.sh Please enter a int that is great then one:55 1540 [root@localhost bin]# bash sumand.sh Please enter a int that is great then one:100 5050 [root@localhost bin]# cat sumand.sh #!/bin/bash read -p "Please enter a int that is great then one:" int echo `seq 1 $int`|tr ' ' '+'|bc
14、輸入起始值A和最後值B,計算從A+(A+1)...+(B-1)+B的總和
[root@localhost bin]# bash sumrandom.sh Please enter the first num:10 please enter the second num:15 75 [root@localhost bin]# bash sumrandom.sh Please enter the first num:15 please enter the second num:10 75 [root@localhost bin]# cat sumrandom.sh #!/bin/bash read -p "Please enter the first num:" first read -p "please enter the second num:" second [ $first -gt $second ] && echo `seq $second $first`|tr ' ' '+'|bc || echo `seq $first $second`|tr ' ' '+'|bc