1. 程式人生 > >2018-2-4Linux系統管理(5)bash腳本編程參數變量及條件判斷

2018-2-4Linux系統管理(5)bash腳本編程參數變量及條件判斷

雙分支 linux系統 scrip 其中 tex pattern linux系統管理 浮點數 順序執行

在上一章當中我們講述了文件系統的管理,以及介紹了一些管理工具的命令,我們現在來總結以下:

   管理工具:mkfs, mke2fs, e2label, tune2fs, dumpe2fs, e2fsck, blkid
      mkfs.xfs, mkfs.vfat, fsck
      mkswap, swapon, swapoff
      mount, umount
      df, du

之後我們也講述了/etc/fatab文件的作用以及該配置文件的格式,但凡寫在該配置文件中的文件系統,開機時能夠自動掛在完成,那麽該配置文件的內容格式為:

   設備    掛載點  文件系統類型    掛載選項    轉儲頻率    自檢次序

我們之前也講過文件系統,在上一次講到文件系統時,首先目錄它也是一個文件,它有元數據和數據,元數據就是一個inode,存放在inode table中,而該文件的數據存儲於data blocks中,每一個data blocks也有其相應的編號,每一個文件在inode當中除了有自己的屬性之外,還有數據塊指針,指向某一個數據塊,順著這個指針可以找到某個數據塊,最後能找到其相關的對應數據,而文件名也是存放在上級目錄,而在data blocks中,存放著的是下級文件或目錄的文件名與其inode的對應關系,形成路徑映射(dentry)。

   文件系統:
     目錄:文件
        元數據:inode, inode table(inode存儲於inode table中)
        數據:data blocks
            下級文件或目錄的文件名與其對應的inode之間的關系;
            
            dentry
            
     文件名:上級目錄;

我們現在補充一下刪除一個文件就是將data block標記為未使用,然後也將inode也標記為未使用。

   刪除文件:將此文件的inode指向的所有data block標記為未使用狀態;將此文件的inode標記為未使用;

而復制和移動,無論是文件或目錄,我們也來總結一下:

   復制和移動文件:
     復制:新建文件;
     移動文件:
         在同一文件系統:改變的僅是其路徑;
         在不同文件系統:復制數據至目標文件,並刪除原始文件;

我們最後在回顧一下符號鏈接,符號鏈接是一種特殊的文件,權限很大,但這不代表指向的原文件會是該鏈接的權限,所以我們來總結一下:

   符號鏈接:
     權限:lrwxrwxrwx
        
         符號鏈接的特點其權限最大為(777),之所以有該權限是因為用戶訪問時,其實並不取決於該符號鏈接的訪問,而是取決於所指向的目標文件的權限;

而硬連接則是指向同一個inode,在同一個分區中施行,也會增加文件本身的引用次數,刪除一個硬鏈接並不耽誤源文件的正常使用,只不過就是引用次數減去一個而已。

一、bash腳本編程

我們此前也也講過bash腳本編程的基礎,首先,該腳本問價的格式為在第一行頂格處寫上#!/bin/bash我們稱之為shebang,如果要是其它的shell腳本可以寫其它的shell解釋器的路徑即可,在腳本中,所有的註釋信息用#來標明,加上註釋信息的好處在讀該腳本時,提示你這個腳本的某一個片段,或者說對整個腳本的功能進行一個了解,為了代碼能夠美觀以及層次分明,我們還可以進行縮進,程序的關聯度不太大時,適度的添加空白行,不然的話,代碼的淩亂讓人為讀寫的話會很麻煩;這也是一種非常好的習慣。

   腳本文件格式:
     第一行,頂格:#!/bin/bash
     註釋信息:#
         如果是其它shell的話,就寫其它shell的路徑解釋器;
     代碼註釋;
     縮進,適度添加空白行;

shell腳本也幾乎是很簡單的一種,與其它語言不同的是,其它的專業語言不止要學習該編程本身的語法格式,還要了解及學習大量的庫調用。對於面向對象的編程語言來說,Java有很多的類庫,Python也有很多的標準庫,為了完成該程序上的某些功能,就要調用該某個庫中的功能模塊。之後,我們還要了解算法和數據結構,不同的編程語言所支持的算法和數據結構也是不同的,而編程也有其編程的思想,我們將學到的該編程的語法格式,轉換為提供解決問題的解決思路。所以,在編程思想的當中,就形成了問題空間和解空間,問題空間是由自然語言提供的,那麽解空間就是將自然語言利用專門的編程語言解決這個問題空間中的問題。

   語言:編程語法格式,庫,算法和數據結構;
   編程思想:
     問題空間 --> 解空間;

我們此前講過腳本基礎的語法格式,以及如何編寫腳本編程,同時也講到了變量,幾乎每一種編程語言都是離不開變量的,雖然shell並沒有什麽內置復雜的數據結構,但這也並不妨礙人為的參與組織出一個復雜的數據結構。但是為了組織出復雜的數據結構,都需要一些內建的數據存儲機制,比如說變量,還有就是多個變量組織在一起的數據結構,比如說數組。那麽變量就是最基本的數據機構,它也是有名字的一種內存空間,而變量則是由小到大進行劃分,有以下幾種:

   變量:
     局部變量;
     本地變量(當前shell);
     環境變量(當前shell及其子shell);
     (按照作用域劃分);
    
     位置參數變量;
     特殊變量;

之後我們也講到了變量中的數據類型,不過需要註意一點,shell並不支持浮點數進行運行,只能借助於其它工具來進行實現。shell是一種弱類型的語言,弱到把所有的數據類型都能處理為字符型。

   數據類型:字符型, 數值型;
     弱類型:字符型;

我們查看一下$PATH的值,在這個值的基礎上添加一個新的路徑,方法如下:

# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin

現在添加一個新的值,例如:/usr/local/apache2/bin/目錄:

# PATH=$PATH:/usr/local/apache2/bin/
# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/usr/local/apache2/bin/

將原來的值補上一個新的值,使用echo命令發現,在原有的值中,添加了一個新的值,這也是一種變量引用,將$PATH空間中的值拿出來,做一些處理之後在放進該空間當中;這也是一種引用之後重新賦值的一種機制。
我們也在後面講到了算數運算,有以下表示方式:

   算數運算:
     +, -, *, /, %, **

     let VAR=expression
     VAR=$[expression]
     VAR=$((expression))
     VAR=$(expr argu1 argu2 argu3)

     註意:有些時候乘法符號需要轉義;

在變量的應用當中,也有變量賦值,在shell當中也支持增強型賦值,所謂增強型賦值就是自身=自身+數值,示例如下:
首先,聲明一個整形變量i,該變量的值為1;然後在該值的基礎上,加1。

# declare -i i=1
# let i=$i+1
# echo $i
2

這是一種平常的方式,我們還有另一種方式,方法如下:

# let i+=1
# echo $i
3

那麽我們總結一下增強型賦值:

   增強型賦值:   
     變量做某種算數運算後回存至此變量中;
        let i=$i+#
        let i+=#
        
    +=, -=, *=, /=, %=

那麽以上的方法,我們使用let來進行描述,要shell腳本當中。我們還有一種更為簡潔的寫法,就是自增和自減的運算。

   自增:
     VAR=$[$VAR+1]
     let VAR+=1
     let VAR++
    
   自減:
     VAR=$[$VAR-1]
     let VAR-=1
     let VAR--

二、腳本測試

我們如果判斷一個用戶是否存在的話,有很多種方法,如果存在的話做什麽樣的處理,反之不存在做什麽樣的處理。所以說條件測試是判斷某需求是否滿足,需要由某種測試機制來實現。

   條件測試:
     判斷某需求是否滿足,需要由測試機制來實現;

專業的測試表達式,需要由測試命令來進行完成的,那麽如何編寫測試表達式以實現所需要的測試,則方法有以下兩種:

   (1) 執行命令,並利用命令狀態返回值來判斷;
     0:成功
     1-255:失敗

我們如果去判斷2是否大於3,肯定在紙上寫2>3,但在shell腳本中,這樣寫肯定是會報錯的,因為shell不能識別該命令以及該字符是否為表達式,我們需要專門的測試表達式來對表達式進行運算。

   (2) 測試表達式
      test EXPRESSION
     [ EXPRESSION ]
     [[ EXPRESSION ]]
    
     註意:EXPRESSION兩端必須有空白字符,否則為語法錯誤;

需要註意的是,判斷一個表達式時,><在shell腳本中並沒有什麽意義,所以,在shell中我們需要專門的測試條件來對表達式進行一個比較條件。
在bash的測試條件的類型共有三種:

   bash的測試類型:
     數值測試
     字符串測試
     文件測試

在這幾種測試當中,首先來說一下數值測試:

   數值測試:數值比較;
     -eq:是否等於;[ $num1 -eq $num2 ]
     -ne:是否不等於;
     -gt:是否大於;
     -ge:是否大於等於;
     -lt:是否小於;
     -le:是否小於等於;

第二種為字符串測試,字符串測試也是一樣的道理,,不過通常可以用來判斷字符串是否為空等。不過需要註意的是,字符測試中,如果沒有該變量,則用雙引號引用形成一個空的字符串,不然會直接報錯,而且要用雙中括號來對字符進行測試表達。

   字符串測試:
     ==:是否等於;
     >: 是否大於;
     <: 是否小於;
     !=:是否不等於;
     =~:左側字符串是否能夠被右側的PATTERN所匹配;
    
     -z "STRING":判斷指定的字符串是否為空;不則為真,不空則假;
     -n "STRING":判斷指定的字符串是否不空;不空為真,空則為假;
    
     註意:
         (1) 字符串要加引號引用;
         (2) 要使用[[ ]]

第三種就是文件測試,主要是實現的功能就是對於文件的存在性、權限等相關測試。一般來說文件測試與之前數值測試與字符串測試是有所不同的,數值與字符串測試主要還是用到的
是雙目測試符號,即兩個中括號,而使用-z和-n的時候,則用單目測試符。對於文件測試來說,常見的一類就是存在性測試。

   文件測試:
     存在性測試:
        -a FILE
        -e FILE
            文件存在性測試,存在則為真,否則為假;

     存在性及類型測試:
        -b FILE:是否存在並且為 塊設備 文件;
         -c FILE:是否存在並且為 字符設備 文件;
        -d FILE:是否存在並且為 目錄 文件;
        -f FILE:是否存在並且為 普通 文件;
        -h FILE 或 -L FILE:是否存在並且為 符號鏈接 文件;
        -p 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

我們可以測試一下該文件是否為文件屬主,並且可讀。

   [ -O FILE ] && [ -r FILE ]

那麽第二種方式就是用於以上那幾種測試的,編寫成測試表達式,而不是命令。

        第二種方式:
            EXPRESSION1 -a EXPRESSION2
            EXPRESSION1 -o EXPRESSION2
            ! EXPRESSION

測試該文件是否屬於當前用戶屬主,並且擁有執行權限。

   [ -O FILE -a -x FILE ]

練習:將當前主機名保存至hostName變量中,如果主機名為空,或者localhost.localdomain,則將其設置為node1.china.org
需要註意的是,如果要做條件連接的話,單中括號要靠譜一點,因為使用雙中括號會報語法錯誤。
對於shell編程來講,每一個命令,它都有一個所謂的狀態返回值,0表示為真,非0表示為假,而即使不是命令表達式,我們可以添加一個test命令等表達式,也能返回0或非0的狀態返回值,同時,我們也可以自定義狀態返回值。

   腳本的狀態返回值:
     默認是腳本中執行的最後一條命令的狀態返回值;
     自定義狀態退出狀態碼:
         exit [n]:n為自己指定的狀態碼;
             註意:shell進程遇到exit時,即會終止,因此整個腳本執行即為結束;

三、向腳本傳遞參數

我們在剛才講到了變量共有五種類型,那麽接下來我們主要講解的就是位置參數變量和局部變量。

3.1 位置參數變量

向腳本傳遞某個參數就是用位置參數變量來實現的,講某個參數傳遞給命令參數進行處理,我們就稱之為位置參數,對於shell來講,的確是通過位置來引用的。假如我們在寫了一個腳本,給它傳遞了兩個參數,那麽我們可以在腳本中直接引用,只要是用戶傳遞的我們就可以在腳本中直接引用該參數。而引用方式就是使用$1和$2等來實現的,當我們運行該腳本時,參數1會自動保存到$1當中。同樣,參數2也是,以此類推。不過需要註意的是,參數十多個的話,要想使用10以上的參數(包括10),需要在10以上的數字加上一個大括號,例如;${10}。

   位置參數變量:
    
     myscripts.sh argu1 argu2
         引用方式:
             $1, $2, ..., ${10}, ${11}

除此之外,位置參數變量還可以做輪替,所謂的輪替就是第二個參數可以踢掉第一個參數,以此類推。默認的話是一次踢掉一個,每一次shift就能踢掉以前的對應的第一位的參數。

#!/bin/bash
#
echo "Please input Arguments: " $1 $2 $3

shift 2

echo "The First Arguments: " $1

3.2 特殊變量

特殊變量,其實就是就被賦予特殊的意義,那麽具體我們來介紹一下。

   特殊變量:
     $0:腳本文件路徑本身;我們可以區其基名或目錄名,例如:basename $0
     $#:腳本參數的個數;
     $*:所有參數;
     $@:所有參數;

如果在剛才的例子中,用戶只給了一個參數,運行的話則會卡住不動,所以我們就得判斷一下用戶的參數如果小於2個,則就給予提示錯誤並退出;

   [ $# -lt 2 ] && echo "At least two arguments." && exit 1

這就是一種判斷語句,但是這樣的話有些人會不理解,那麽接下來我們寫一個真正的判斷語句。

四、過程式編程代碼執行程序

對於過程式的編程語言執行分為三部分,第一個是順序執行,也稱為逐條執行,自上而下一個挨著一個的進行。另一種就是選擇執行,選擇執行就是在代碼上存在一個或多個分支,執行時只執行其中一個。還有一種就是循環執行,是將某一個代碼片段要執行0次、1次或多次,通過這以上三種執行機制,就可以設計算法來完成一些復雜的代碼的功能。

   過程式編程語言的代碼執行順序:
     順序執行:逐條執行;
    
     選擇執行:
         代碼有一個分支;條件滿足時才會執行;
         兩個或以上的分支;只會執行其中一個滿足條件的分支;
        
     循環執行:
         代碼片段(循環體)要執行0、1或多個來回;

我們現在主要講的是選擇執行,對於每一種編程語言來說都會用到,循環也亦是如此,在選擇執行中,如果這個測試條件為真,則執行該條件內為真的的程序代碼。測試條件為假時,則跳過執行該分支語句,以上這是單分支的if語句。
那麽是雙分支的if語句也是基本相同,當條件為真時,則執行該條件內為真的程序代碼;測試條件為假時,則執行在該條件為假的程序代碼。

   選擇執行:
     單分支的if語句:
         if 測試條件; then
             代碼分支
         fi
    
     雙分支的if語句:
         if 測試條件; then
            條件為真時執行的分支
         else
            條件為假時執行的分支
         fi

示例:通過參數傳遞一個用戶名腳本,此用戶不存在時,則添加。

#!/bin/bash
# Author: [email protected]
# Descriptions: Add username
# Date: 2018-02-06

[ -z "$1" ] && echo "Please add username." && exit 2

if ! id -u $1 &> /dev/null; then
    useradd $1 &> /dev/null
    echo "$1" | passwd --stdin $1
else
    echo "$1 extsis."
fi

需要註意的是,只有寫表達式時才會用到表達測試符,而用命令作為測試條件的話則不用。


2018-2-4Linux系統管理(5)bash腳本編程參數變量及條件判斷