腳本入門:shell變量和條件測試

寫腳本是運維行業中最有趣的一部分(我是這麼覺得),一個功能強大的腳本可以省去許多的時間(如果以後都是自動化運維,我們該幹什麼呢?)。腳本的用途主要有:自動化處理、執行系統管理和故障排除、一鍵部署管理應用程序、批量處理文本文件。

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變量類型:

按照變量的內容可以分爲:字符型和數值型

字符型:

數值型:

按照變量的作用範圍分:

  • 本地變量:變量只對當前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

wKioL1eulM2hM7XkAAAT9CxL2uE787.png

當執行上面的命令時,括號生成一個新的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

wKioL1evD_LgOiGbAAEaK1ixifE284.png

可以看到,磁盤最大利用率一直在增大,當大於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



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章