Shell腳本編程基礎

什麼是Shell

    操作系統最外層的程序,shell通過提示符讓用戶輸入,向操作系統解釋該輸入,然後處理來自操作系統的任何結果輸出來,管理用戶與操作系統之間的交互。

    Shell是一個用戶跟操作系統之間的一個命令解釋器。Shell是用戶與Linux操作系統之間溝通的橋樑。用戶可以輸入命令執行,又可以利用 Shell腳本編程去運行。

    

爲什麼要用到shell

shell是一個交互式程序,當用戶輸入一條命令,shell就解釋一條,一次只處理一條命令。如果我們一些複雜操作,逐個敲命令工作量就會增大,因此,我們可以事先寫一個腳本,在腳本中寫入多條命令,讓shell一次性把這些命令全都執行完畢,而不必一條一條的敲


常見的shell種類

    /bin/sh,/bin/csh,/bin/ksh,/bin/bash等,bash是大多數linux默認的shell程序,因此,在日常工作中被廣泛使用



編程基礎:

    程序:指定+數據

        指令:由程序文件提供

        數據:IO設備、文件、管道、變量等


    程序編程風格:

        過程式:以指令爲中心,數據服務於指令

        對象式:以數據爲中心,指令服務於數據


    編程語言:

        強類型:必須實現聲明定義的變量及變量類型。如 java、python

        弱類型:無需事先聲明變量,默認均爲字符串類型,可直接調用變量。如 shell

    

    程序運行方式:

        編譯運行:源代碼 --> 編譯器編譯 --> 可執行的二進制文件

        解釋運行:源代碼 --> 運行時啓動解釋器,邊解釋邊運行


    如何編寫shell腳本:

        腳本文件的第一行,頂格:給出shebang,解釋器路徑,用於指明解釋執行當前腳本的解釋器程序文件

            常見的解釋器:

                #!/bin/bash

                #!/usr/bin/perl

                #!/usr/bin/python

        編寫第一個shell腳本:

            [root@CentOS6 bin]# cat first.sh 
            #!/bin/bash
            #author: xiaozhai
            #version: 1.0
            #date: 2016-8-11
            #description: first shell script
            
            echo "Hello,world"
            [root@CentOS6 bin]# bash first.sh 
            Hello,world
            [root@CentOS6 bin]#

        一個好的shell腳本會標明、作者、日期、版本、作用,這是一個良好的習慣,不然我們代碼寫到百行的時候,過幾個月或幾年,回過頭來看,又能知道自己寫的是什麼腳本呢

        

    如何運行shell腳本:

        (1)賦予腳本文件執行權限,並直接運行腳本

            chmod +x SCRIPT_FILE

            ./PATH/TO/SCRIPT_FILE

        (2)直接運行解釋器,將腳本以命令參數傳遞給解釋器程序

            bash /PATH/TO/SCRIPT_FILE

    

    變量:指向內存的命令空間

        變量名+指向的內存空間

        變量賦值:VAR_NAME=value

        變量類型:存儲格式、表示數據範圍、參與的運算

            字符型

            數值:整型、浮點型(shell不支持浮點型)

         變量替換:把變量名出現的位置替換爲指定的內存空間中的數據

        變量引用:${var_name},$var_name

        變量名命名規則:

            1.不能與系統中定義過的變量同名,不能與bash中的關鍵字同名,如 if case等等

            2.只能包含數字、下劃線、字母

            3.要做到見名只義

       bash變量類型:

            本地變量:僅對當前shell進程生效

            環境變量:對當前shell以及子shell生效

            局部變量:僅對shell進程中某一代碼片段生效

            位置變量:腳本執行時傳遞給腳本的參數

            特殊變量:shell內置的有特殊功用的變量

            

            本地變量:

                變量賦值:name='value'

                變量引用:${name},$name

                    "":變量名會被替換爲其值

                    '':變量名不會替換爲值,當做字符串來處理

                查看變量:set

                撤銷變量:unset var_name

            

            環境變量:

                變量賦值:

                    (1)export name='value'

                    (2)name='value'

                      export name

                    (3)declare -x name='value'

                    (4)name='value'

                      declare -x name

                變量引用:${name},$name

                查看環境變量:export、declare、env、printenv

                撤銷變量:unset

                bash有許多內置的環境變量:PATH,SHELL,USER,UID,GID,HISTSIZE,HISTFILESIZE,HOME,PWD,OLDPWD,HISTFILE,HISTCONTROL等

            

            只讀變量:只能聲明,不可修改刪除

                readonly var_name

                declare -r var_name

                

            位置變量:在執行腳本時,傳遞給腳本的參數

                $1,$2,...${10}、${11}對應1、2個參數

            

            特殊變量:shell內置變量

                $?:存放上一條命令的執行狀態返回碼

                $#:執行腳本時傳遞給腳本的參數個數

                $*:傳遞給腳本的所有參數,會把這些參數當做一個字符串

                $@:傳遞給腳本的所有參數,把每個參數當做一個字符串來處理

                $0:此腳本的名稱

                $$:當前shell程序的PID



算術運算:

    +, -, *, /, %, **

    算術運算格式:

        (1)let VAR=Expression

        (2)VAR=$[Expression]

        (3)VAR=$((Expression))

        (4)VAR=$(expr $ARG1 $OP $ARG2)

        (5)echo "Expression" | bc

    bash內建隨機數生成器:$RANDOM(1-32767)

    增強型賦值:+=, -=, *=, /=, %=

        例如:let COUNT+=3

            COUNT等於自身加3

    自增,自減:

        let var+=1

        let var++

        let var-=1

        let var--

    i++運算後加1,i--運算後減1

    ++i運算前加1,--i運算前減1



邏輯運算:

    運算數:

        true:真,用1表示

        false:假,用0表示


    與運算:兩個條件同時滿足,則爲真

       1 && 1 = 1

        1 && 0 = 0

        0 && 1 = 0

        0 && 0 = 0

        

    或運算:兩個條件有一個爲真,則爲真

        1 || 1 = 1

        1 || 0 = 1

        0 || 1 = 1

        0 || 0 = 0

            

    :取反

        ! 1 = 0

        ! 0 = 1


    短路法則:

        ~]# COMMADN1 && COMMADN2

        COMMADN1爲“假”,COMMADN2將不會執行

        否則,COMMADN1爲“真”,COMMADN2必須執行

        

        ~]# COMMADN1 || COMMADN2

        COMMAND1爲“真”,則COMMADN2不會被執行

        否則,COMMADN1爲“假”,則COMMAND2必須執行

    

    異或:^

        異或的兩個值,相同爲假,不同則真


退出狀態碼:

    進程使用退出狀態碼來報告命令的執行結果,成功或失敗

        $?:上一條命令的執行狀態返回碼

        成功:0

        失敗:1-255

    自定義狀態返回值:

       exit#:#爲自己指定的狀態返回碼

       注意:當腳本中遇到exit時,將終止整個腳本運行,默認是腳本中執行的最後一條命令的狀態返回值



條件測試:

    判斷某個需求是都滿足,如果滿足則執行相應的操作,需要由測試機制來實現

     如何編寫測試表達式以實現所需的測試:

        (1)評估布爾聲明,以便用在條件測試中

            若真,則返回0

            若假,則返回非1

        (2)測試表達式:

            test EXPRESSION

            [ EXPRESSION ]

            ` EXPRESSION `

            

            注意:EXPRESSION前後兩端要有空格,否則報錯

    

    bash測試類型:

        數值測試:

        字符串測試:

        文件測試:

    

        數值測試:數值比較

            -eq:測試是否等於

            -gt:是否大於

            -lt:是否小於

            -ge:是否大於等於

            -le:是否小於等於

            -ne:是否不等於

        

        字符串測試:字符比較

            ==:測試字符是否相等

            !=:是否不等於

            =~:右側的字符是否能被左側的PATTERN所匹配

            >:是否大於

            <:是否小於

            -z:測試指定字符串是否爲空,空爲真,否則爲假

            -n:測試指定的字符串是否不不空,不空則真,否則爲假

            

            注意:字符串比較實用` `

        

       文件測試:

            存在性測試:

                [ -a FILE ]:測試文件是否存在

                [ -e FILE ]

            存在性及類型測試:

                [ -b FILE ]:測試文件是否爲快設備文件

                [ -c FILE ]:測試文件是否爲字符設備

                [ -d FILE ]:測試文件是否爲目錄文件

                [ -f FILE ]:測試文件是否爲普通ASCII文件

                [ -p FILE ]:側是文件是否爲管道文件

                [ -h FILE ]或[ -L FILE ]:測試文件是否爲符號鏈接文件

                [ -S FILE ]:測試文件是否爲套接字文件

            文件權限測試:      

                [ -r FILE ]:測試當前用戶是否對指定文件可讀

                [ -w FILE ]:測試當前用戶是否對文件可寫

                [ -x FILE ]:測試當前用戶是否對文件可執行

            特殊權限測試:

                [ -u FILE ]:測試文件是否具有SUID權限

                [ -g FILE ]:測試文件是否具有SGID權限

                [ -k FILE ]:測試文件是否有Sticky權限

            測試文件中是否有內容:

                [ -s FILE ]:測試文件中是否有內容,有爲真,否則假

            時間戳:

                [ -N FILE ]:文件自從上次讀取後是否被修改過

            從屬關係測試:

                [ -O FILE ]:測試當前用戶是否是文件的屬主

                [ -G FILE ]:測試當前用戶是否是文件屬組

            雙目測試:

                [ FILE1 -ef FILE2 ]:FILE1與FILE2是否指向同一個文件系統的相同inode的硬鏈接

                [ FILE1 -nt FILE2 ]:FILE1是否新於FILE2

                [ FILE1 -ot FILE2 ]:FILE1是否舊於FILE2



組合條件測試:

    第一種方式:

        COMMAND1 && COMMAND2

        COMMAND1 || COMMAND2

        ! COMMAND

        

        [ -x FILE ] && [ -r FILE ]

    

    第二種方式:

        -a:與

        -o:或

        !:非


        [ EXPRESSION1 -a EXPRESSION2 ]

        [ EXPRESSION1 -o EXPRESSION2 ]

        [ ! EXPRESSION ]




練習:

1.編寫腳本/root/bin/systeminfo.sh,顯示當前主機系統信息,包括主機名,IPv4地址,操作系統版本,內核版本,CPU型號,內存大小,硬盤大小。

[root@CentOS6 bin]# cat systeminfo.sh     #腳本內容
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 顯示當前系統信息包括主機名,IPv4地址,操作系統版本,內核版本,CPU型號,內存大小,硬盤大小
 
Hostname=$(hostname)
IP=$(ifconfig | sed -n '2p' | cut -d: -f2 | cut -d' ' -f1)
OS=$(lsb_release | grep "Description" | cut -d: -f2 | sed 's@^[[:space:]]\+@@')
Kernel=$(uname -r)
CPU=$(cat /proc/cpuinfo | grep "model name" | cut -d: -f2 | sed 's@^ @@')
MEM=$(free -h | tr -s ' ' | cut -d' ' -f2 | sed -n '2p')
DISK=$(fdisk -l | grep "Disk /dev/sd" | cut -d: -f2 | cut -d, -f1)
 
echo -e "\033[32mShow system information.\033[0m"
echo "Hostname: $Hostname"
echo "IPv4 address: $IP"
echo "OS version: $Kernel"
echo "CPU model: $CPU"
echo "Mem size: $MEM"
echo "Disk size: $DISK"
[root@CentOS6 bin]# vim systeminfo.sh 
[root@CentOS6 bin]# bash systeminfo.sh     #運行結果
Show system information.
Hostname: CentOS6.localdomain
IPv4 address: 10.1.252.233
OS version: 2.6.32-642.el6.x86_64
CPU model: Intel(R) Core(TM) i7-6500U CPU @ 2.50GHz
Mem size: 980M
Disk size:  107.4 GB
[root@CentOS6 bin]#

2.編寫腳本/root/bin/backup.sh,可實現每日將/etc/目錄備份到/root/etcYYYY-mm-dd中

[root@CentOS6 bin]# cat backup.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 將/etc/目錄下的內容備份到/root/etcYYYY-mm-dd中
 
FILE="/root/etc`date +%F`"
 
cp -rpf /etc/ $FILE 
 
echo "$FILE file backup finished."
[root@CentOS6 bin]# bash backup.sh 
/root/etc2016-08-12 file backup finished.
[root@CentOS6 bin]# ls -d ../etc2016-08-12/
../etc2016-08-12/
[root@CentOS6 bin]#


3.編寫腳本/root/bin/disk.sh,顯示當前硬盤分區中空間利用率最大的值

[root@CentOS6 bin]# cat disk.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 顯示當前硬盤分區中空間利用率最大的值
 
Max_Used=$(df -lhTP | sed -r 's@.* ([0-9]+%).*@\1@' | sed '1d' | sort | tail -1)
 
echo "分區空間利用率最大爲$Max_Used"
[root@CentOS6 bin]# bash disk.sh 
分區空間利用率最大爲19%
[root@CentOS6 bin]#


4.編寫腳本/root/bin/links.sh,顯示正連接本主機的每個遠程主機的IPv4地址和連接數,並按連接數從大到小排序

[root@CentOS6 bin]# cat links.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 顯示正連接本主機的每個遠程主機的IPv4地址和連接數,並按連接數從大到小排序
 
echo -e "當前連接本機的遠程主機IP\n"
echo -e "連接次數     IP地址"
 
netstat -tn | tr -s ' ' | cut -d' ' -f5 | cut -d: -f1 | sed '1,2d' | sort | uniq -c | sort -n -t' ' -k1
[root@CentOS6 bin]# bash links.sh 
當前連接本機的遠程主機IP
連接次數     IP地址
      2 10.1.250.60
      3 10.1.253.23
[root@CentOS6 bin]#


5.寫一個腳本/root/bin/sumid.sh,計算/etc/passwd文件中的第10個用戶和第20用戶的ID之和

[root@CentOS6 bin]# cat sumid.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#descriptions: 計算/etc/passwd文件中的第10個用戶和第20用戶的ID之和
 
FILE="/etc/passwd"
 
ID_10=$(cat $FILE | sed -n '10p' | cut -d: -f3)
ID_20=$(cat $FILE | sed -n '20p' | cut -d: -f3)
 
SUMID=$[$ID_10+$ID_20]
echo "$FILE file 10 user and 20 user uid sum is $SUMID."
[root@CentOS6 bin]# 
[root@CentOS6 bin]# bash sumid.sh 
/etc/passwd file 10 user and 20 user uid sum is 180.
[root@CentOS6 bin]#


6.寫一個腳本/root/bin/sumfile.sh,統計/etc, /var, /usr目錄中共有多少個一級子目錄和文件

[root@CentOS6 bin]# cat sumfile.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 統計/etc, /var, /usr目錄中共有多少個一級子目錄和文件
 
ETC=$(ls -A /etc/ | wc -l)
VAR=$(ls -A /var/ | wc -l)
USR=$(ls -A /usr/ | wc -l)
 
SUMFILE=$[$ETC+$VAR+$USR]
echo "/etc/ /var/ /usr/ A total of $SUMFILE files"
[root@CentOS6 bin]# 
[root@CentOS6 bin]# bash sumfile.sh 
/etc/ /var/ /usr/ A total of 296 files
[root@CentOS6 bin]#


7.寫一個腳本/root/bin/argsnum.sh,接受一個文件路徑作爲參數;如果參數個數小於1,則提示用戶“至少應該給一個參數”,並立即退出;如果參數個數不小於1,則顯示第一個參數所指向的文件中的空白行數

[root@CentOS6 bin]# cat argsnum.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 接受一個文件路徑作爲參數;如果參數個數小於1,則提示用戶“至少應該給一個參數”,並立即退出;如果參數個數不小於1,則顯示第一個參數所指向的文件中的空白行數
 
[ $# -lt 1 ] && echo "At least one parameter." && exit 1
 
SpaceLine=$(grep "^$" $1 | wc -l)
echo "$1 spaceline is $SpaceLine."
[root@CentOS6 bin]# 
[root@CentOS6 bin]# bash argsnum.sh     #沒有給出參數,執行結果提示至少一個參數
At least one parameter.
[root@CentOS6 bin]# bash argsnum.sh /etc/inittab     #給出一個文件路徑,顯示此文件的空白行數
/etc/inittab spaceline is 8.
[root@CentOS6 bin]#


8.寫一個腳本/root/bin/hostping.sh,接受一個主機的IPv4地址做爲參數,測試是否可連通。如果能ping通,則提示用戶“該IP地址可訪問”;如果不可ping通,則提示用戶“該IP地址不可訪問”

[root@CentOS6 bin]# cat hostping.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 接受一個主機的IPv4地址做爲參數,測試是否可連通。如果能ping通,則提示用戶“該IP地址可訪問”;如果不可ping通,則提示用戶“該IP地址不可訪問”
 
[ $# -lt 1 ] && echo "At least one parameter." && exit 1
 
ping -c 1 -W 1 $1 &> /dev/null && echo "$1 IP accessible" || echo "$1 IP not accessible."
[root@CentOS6 bin]# 
[root@CentOS6 bin]# bash hostping.sh 10.1.1.1    
10.1.1.1 IP not accessible.        #IP不可訪問
[root@CentOS6 bin]# 
[root@CentOS6 bin]# bash hostping.sh 10.1.252.233
10.1.252.233 IP accessible        #IP可訪問
[root@CentOS6 bin]#


9.chmod -rw /tmp/file1,編寫腳本/root/bin/per.sh,判斷當前用戶對/tmp/fiile1文件是否不可讀且不可寫

[root@CentOS6 /]# cat per.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 判斷當前用戶對/tmp/fiile1文件是否不可讀且不可寫
 
FILE="/tmp/file1"
 
[ ! -r $FILE -a ! -w $FILE  ] && echo "yes" || echo "no"    #如果不可讀也不可寫,則輸出yes,否則輸出no
[root@CentOS6 /]# 
[root@CentOS6 /]# ll /tmp/file1     #首先查看/tmp/file1文件的權限,爲640
-rw-r-----. 1 root root 0 Aug 11 22:43 /tmp/file1
[root@CentOS6 /]# 
[root@CentOS6 /]# bash per.sh 
no        #root用戶對此文件可讀寫,輸出no
[root@CentOS6 /]# su - zhai    #切換至其他用戶
[zhai@CentOS6 ~]$ whoami
zhai
[zhai@CentOS6 ~]$ bash /per.sh 
yes        #zhia用戶對此文件不可讀也不可寫,輸出yes
[zhai@CentOS6 ~]$


10.編寫腳本/root/bin/nologin.sh和login.sh,實現禁止和充許普通用戶登錄系統。

用戶不可登錄nologin
[root@CentOS6 bin]# cat nologin.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 實現禁止普通用戶登錄系統
 
FILE="/etc/nologin"
 
([ ! -e $FILE ] && touch $FILE && echo "create $FILE finished. ") || echo "$FILE exists."    #判斷/etc/nologin文件是否存在,如果不存在,則創建
[root@CentOS6 bin]# bash nologin.sh 
create /etc/nologin finished.     #提示創建文件成功,此時普通用戶已經不可登錄系統了
[root@CentOS6 bin]#


用戶可登錄login
[root@CentOS6 bin]# cat login.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 實現允許普通用戶登錄系統
 
FILE="/etc/nologin"
 
([ -e $FILE ] && rm -rf $FILE && echo "delete $FILE finished.") || echo "$FILE not exists."    #判斷/etc/nologin文件是否存在,如果存在就刪除
[root@CentOS6 bin]# 
[root@CentOS6 bin]# bash login.sh 
delete /etc/nologin finished.    #提示刪除文件完成,此時普通用戶就可以登錄到系統了
[root@CentOS6 bin]#

11.寫一個腳本/root/bin/hostping.sh,接受一個主機的IPv4地址做爲參數,先判斷是否合格IP,否,提示IP格式不合法並退出,是,測試是否可連通。如果能ping通,則提示用戶“該IP地址可訪問”;如果不可ping通,則提示用戶“該IP地址不可訪問”

[root@CentOS6 bin]# cat hostping.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 寫一個腳本/root/bin/hostping.sh,接受一個主機的IPv4地址做爲參數,先判斷是否合格IP,否,提示IP格式不合法並退出,是,測試是否可連通。如果能ping通,則提示用戶“該IP地址可訪問”;如果不可ping通,則提示用戶“該IP地址不可訪問”
 
[ $# -lt 1 ] && echo "At least one parameter." && exit 1
 
echo "$1" | grep -E -q "\<([1-9]|[1-9][0-9]|1[0-9]{2}|2[0-1][0-9]|22[0-3])\>\.(\<([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\>\.){2}\<([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\>"
RETVAL=$?
[ $RETVAL -ne 0 ] && echo "$1 not is IP address." && exit 5
 
(ping -c 1 -W 1 $1 &> /dev/null && echo "$1 IP accessible") || (echo "$1 IP not accessible" && exit 7)
[root@CentOS6 bin]# 
[root@CentOS6 bin]# bash hostping.sh 255.1.1.1    #給定非IP地址
255.1.1.1 not is IP address.        
[root@CentOS6 bin]# bash hostping.sh 10.1.252.255    #給定正確IP地址
10.1.252.255 IP accessible        #此IP可訪問
[root@CentOS6 bin]# bash hostping.sh 10.1.252.252
10.1.252.252 IP not accessible    #此IP不可訪問
[root@CentOS6 bin]#


12.計算1+2+3+…+100的值

[root@CentOS6 bin]# cat sum2.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 計算1到100之間所有整數之和
 
SUM=$(echo {1..100} | tr ' ' '+' | bc)
 
echo "1到100之間的所有整數之和爲$SUM."
[root@CentOS6 bin]# 
[root@CentOS6 bin]# bash sum2.sh 
1到100之間的所有整數之和爲5050.
[root@CentOS6 bin]#


13.計算從腳本第一參數A開始,到第二個參數B的所有數字的總和,判斷A是否大於B,否提示錯誤並退出,是則計算

[root@CentOS6 bin]# cat sum3.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 計算從腳本第一參數A開始,到第二個參數B的所有數字的總和,判斷B是否大於A,否提示錯誤並退出,是則計算之
 
[ $# -ne 2 ] && echo "Unknown Argument." && exit 1
! expr $1 + $2 &> /dev/null && echo "Non integer parameter." && exit 2
[ $1 -gt $2 ] && echo "$1 greater $2." && exit 3
 
SUM=$(seq $1 $2 | tr '\n' ' ' | tr ' ' '+' | sed -r 's@\+$@\n@' | bc)
 
echo "$1至$2之間所有整數之和爲$SUM."
[root@CentOS6 bin]# 
[root@CentOS6 bin]# bash sum3.sh     #腳本後不跟參數,提示未知參數
Unknown Argument.
[root@CentOS6 bin]# bash sum3.sh 1 a    #給出的參數不是整數
Non integer parameter.
[root@CentOS6 bin]# bash sum3.sh 10 1    #$1大於$2
10 greater 1.
[root@CentOS6 bin]# bash sum3.sh 1 99    #正確執行腳本
1至99之間所有整數之和爲4950.
[root@CentOS6 bin]#





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