2018-8-28-bash數組與字符串處理

在上次的課程中咱們講述了函數和case語句,case語句的語法格式爲:python

case $VARIABLE in
PAT1)
    分支1
    ;;
PAT2)
    分支2
    ;;
*)
    分支n
    ;;
esac

case是一個多分支的判斷語句,與多個值進行比較時,case是比if語句要好用的多,在這裏的PAT可以使用GLOB通配符,以及|爲或者之意,而若是作非字符串的等值比較時,則case語句就沒法取代。linux

緊接着又講到了函數,函數只能是被調用才能被執行,而給定函數名則便可進行調用,可接受參數,使用$1$2等位置變量進行聲明,函數的組成目的是可以實現結構化編程,同時還能完成代碼重用,函數的使用可引入了新的上下文,可以使用局部變量,它的生命週期是當函數的生命週期結束,則會提早自動銷燬,那麼其函數的定義格式爲:編程

function f_name {
    函數體
}

f_name() {
    函數體
}

局部變量:local VARIABLE

1、數組

接下來咱們開始講數組,咱們的程序是由指令和數據組成,對於bash來說,指令就是編程當中的語法關鍵字,加上各類能調用的系統命令及定義的函數等均可以理解爲指令,而數據對於bash來說,存放在變量和文件,當咱們要存儲多個類型相同的數據時,就可使用一個叫數組的東西用來存儲。數組

因此說數組和變量同樣,只不過不一樣的是存儲多個元素連續的內存空間,而變量是存儲單個元素的內存空間。bash

數組也有數組名,且整個數組只有一個名字,它也是指向連續內存空間的開始處的起始位置,數組當中存儲多個元素,若是想引用某個特定元素的話,就要使用另一種機制找到該元素,那麼這時候就要使用數組中的下標,也稱爲索引機制。dom

最開始,也就是第一個元素,咱們用0來進行表示,然後咱們就使用1,2,3,4分別來表示。ide

數組:
    程序=指令+數據
        指令:
        數據:變量、文件;

    變量:存儲單個元素的內存空間;
    數組:存儲多個元素的連續的內存空間;
        數組名:整個數組只有一個名字;
        數組索引:編號從0開始;
            數組名[索引]

以上數組名[索引]這種方式就能夠引用數組中的元素,對於bash而言,其真正使用的格式爲:${ARRAY_NAME[INDEX]},這裏須要注意的是,花括號可不可以省略,由於去掉了花括號,$ARRAY_NAME就會被認爲這是個變量,而並不會理解爲這是一個數組的索引。因此對於數組以及數組的元素而言,則必須加上{}函數

對於bash-4.0之後的版本,對於數組的索引也能夠支持自定義的索引格式,也稱之爲稀疏格式,不只是0,1,2,...定義的數字格式。而這類數組咱們就稱之爲關聯數組工具

注意:bash-4及以後的版本,支持自定義索引格式,而不只是0,1,2,... 所定義的數字格式。
    此類數組稱之爲"關聯數組";

1.1 聲明、賦值及引用數組的方式

對於數組來說,是須要事先進行聲明的,若是不聲明,極可能會看成單個變量來使用。post

聲明數組:
    declare -a NAME: 聲明索引數組;
    declare -A NAME: 聲明關聯數組;

以上就是數組聲明的兩種方式,那麼數組就是在內存空間當中擁有多個連續的空間且分別可以存儲多個連續的數據,每一個數據都是獨立的單元,只是沒有獨立的名稱,須要用數組名稱來來進行引用,但爲了引用每個元素,咱們使用下標來進行,可理解爲數組的標識,而下標可有兩種方式,第一種是以數值的形式,咱們稱之爲索引數組,第二種使用的是字符串來進行的下標,咱們稱之爲關聯數組。

而使用數組時,如何向數組中的元素進行賦值方法有不少種。

數組中元素的賦值方式:
    (1) 一次只賦值一個元素:
        ARRAY_NAME[INDEX]=VALUE

    (2) 一次賦值所有元素:
        ARRAY_NAME=("VAL1", "VAL2", "VAL3" ...)

    (3) 只賦值特定元素:
        ARRAY_NAME=([0]="VAL1", [3]="VAL5")

        注意:bash支持稀疏格式的數組;

    (4) read -a ARRAY_NAME

示例:

# animals[0]=pig
# animals[1]=bear
# echo ${animals[0]}
pig

這就是數組元素賦值,無需事先聲明,這種一次只是賦值一個元素,若是echo回顯沒有使用下標的話,默認顯示爲0元素的數值。

引用數組中的元素:${ARRAY_NAME[INDEX]}
    注意:引用時,只給數組名,表示引用下標爲0的元素;

這是第一種賦值方式,那麼第二種一次賦值多個元素的方式爲:

# weekdays=("Monday" "Tuesday" "Wednesday" "Tourday")
# echo ${weekdays[1]}
Tuesday

第三種是是賦值特定的,就是能夠在挑選元素賦值,就是稀疏格式的數組,所謂稀疏格式就是:每一個元素的那個值都不必定會存在。

# sword=([0]="Yitian" [3]="Tudong" [5]="Gelu")
# echo ${sword[1]}

# echo ${sword[3]}
Tudong

最後就是使用read -a來給數組進行賦值,自動賦值給多個元素。

# read -a jianghu
Yuebuqun Dongfangbubai Linghuchong Qianchaotaijian
# echo ${jianghu[1]}
Dongfangbubai

若是咱們想使用關聯數組的話,示例以下:

# world[us]="america"
# echo "${world[us]}"
america
# world[uk]="United Kingdom"
# echo "${world[uk]}"
United Kingdom

咱們本身直接給出字符串下標看成索引,來實現數組中的元素也是能夠的,而這種就叫作關聯數組。

那麼引用數組中的元素咱們也展現過了,爲${ARRAY_NAME[INDEX]},須要注意的是,引用只給出數組名的話,會表示引用的爲下標爲0的元素。

引用數組中的元素:${ARRAY_NAME[INDEX]}
    注意:引用時,只給數組名,表示引用下標爲0的元素;

另外咱們也能夠引用腳本時的參數同樣,來引用數組中的全部元素及個數,說白了就是引用數組的長度。

數組的長度(數組中元素的個數)
    ${#ARRAY_NAME[*]}
    ${#ARRAY_NAME[@]}

示例:生成10個隨機數,並找出其中的最大值和最小值。

#!/bin/bash
#

declare -a rand
declare -i max=0

for i in {0..9}; do
    rand[$i]=$RANDOM
    echo "${rand[$i]}"
    [ ${rand[$i]} -gt $max ] && max=${rand[$i]}
done

echo "Max:$max"

示例:定義一個數組,數組中元素是/var/log/目錄下全部以.log結尾的文件;統計其下標爲偶數的文件中的行數之和。

#!/bin/bash
#

declare -a files
files=(/var/log/*.log)

declare -i lines=0

for i in $(seq 0 $[${#files[*]}-1]); do
    if [ $[$i%2] -eq 0 ]; then
        let lines+=$(wc -l ${files[$i]} | cut -d ' ' -f1)
    fi
done

echo "Lines: $lines."

引用數組中的元素個數咱們剛纔講過了,但若是要引用數組中的全部元素。

${ARRAY_NAME[*]}
${ARRAY_NAME[@]}

那麼咱們接下來開始講述一下數組元素切片,那個切片的目的就是能夠返回有限的幾個元素。
示例:

# files=(/etc/[Pp]*)
# echo "${files[*]}"
/etc/PackageKit /etc/pam.d /etc/passwd /etc/passwd- /etc/pbm2ppa.conf /etc/pinforc /etc/pkcs11 /etc/pki /etc/plymouth /etc/pm /etc/pnm2ppa.conf /etc/polkit-1 /etc/popt.d /etc/postfix /etc/ppp /etc/prelink.conf.d /etc/printcap /etc/profile /etc/profile.d /etc/protocols /etc/pulse /etc/purple /etc/python

咱們能夠保留某些個元素,而切片是用偏移量offset,意味這偏移過去幾個元素並取幾個元素number

數組元素切片:${ARRAY_NAME[@]:offset:number}
    offset:要路過的元素個數;
    number:要取出的元素個數;省略number時,表示偏移量以後的全部元素;

示例:

# echo ${files[@]:3:4}
/etc/passwd- /etc/pbm2ppa.conf /etc/pinforc /etc/pkcs11
# echo ${files[@]:3}
/etc/passwd- /etc/pbm2ppa.conf /etc/pinforc /etc/pkcs11 /etc/pki /etc/plymouth /etc/pm /etc/pnm2ppa.conf /etc/polkit-1 /etc/popt.d /etc/postfix /etc/ppp /etc/prelink.conf.d /etc/printcap /etc/profile /etc/profile.d /etc/protocols /etc/pulse /etc/purple /etc/python

對於數組而言,咱們還能夠向數組中追加元素。給明元素下標,可是若是不知道數組有多少元素的話,就要引用數組的長度。

向非稀疏格式數組中追加元素:
    ARRAY_NAME[${#ARRAY_NAME[*]}]=
# animals[${#animals[*]}]=admin
# echo "${animals[@]}"
pig dog admin

那麼刪除數組中的某個元素的話咱們使用unset指令,與以前的撤銷變量差很少,只不過不一樣的是撤銷變量的話須要使用$,並非撤銷元素中的值而已,而在數組中就直接輸入那個數組中的那個元素就能夠。

刪除數組中的某元素:
    unset ARRAY[INDEX]
# unset animals[0]
# echo "${animals[@]}"
dog  admin

剛纔講到過關聯數組是什麼,關聯數組是以用字符爲下標的形式來進行,需先聲明其關聯數組然後可進行賦值,那麼它的聲明方式及格式爲:

關聯數組:
    declare -A ARRAY_NAME
        ARRAY_NAME=([index_name1]="value1" [index_name2]="value" ...)

2、bash的內置字符串處理工具

接下來咱們就說一下經常使用的字符串處理工具,首先第一個就是字符串切片工具,與數組切片差很少,偏移過去取出那幾個字符。

字符串切片:
    ${var:offset:number}
        取字符串的子串;
        取字符串的最右側的幾個字符:${var: -length}
            注意:冒號後必須有一個空白字符;
# name=Jerry
# echo ${name: 1}
erry
# echo ${name: 2}
rry
# echo ${name:2:2}
rr
# echo ${name: -4}
erry

還有一種就是基於模式去字符串的子串。

基於模式取子串:
    ${var#*word}:其中word是指定的分隔符;功能:自左而右,查找var變量所存儲的字符串中,第一次出現的word分隔符,刪除字符串開頭至此分隔符之間的全部字符;
    ${var##word}:其中word是指定的分隔符;功能:自左而右,查找var變量所存儲的字符串中,最後一次出現的word分隔符,刪除字符串開頭至此分隔符之間的全部字符;
# mypath="/etc/init.d/functions"
# echo "${mypath#*/}"
etc/init.d/functions

# echo "${mypath##*/}"
functions

從以上兩個實例看得出,第一個以/爲分隔符,表示自左而右,查找第一次的分隔符/,把整個開頭的分隔符到第一次出現的/將其刪除。

而第二個實例就是自左而右找出最後一個/分隔符,將第一次開頭的分隔符到最後一個開頭的分隔符之間的範圍將其刪除,可做爲取路徑基名。

除了自左而右,咱們還能夠自右而左。

${var%word}:其中word是指定的分隔符;功能:自右而左,查找var變量所存儲的字符串中,第一次出現的word分隔符,刪除此分隔符至字符串尾部之間全部的字符;
${var%%word}:其中word是指定的分隔符;功能:自右而左,查找var變量所存儲的字符串中,最後一次出現的word分隔符,刪除此分隔符至字符串尾部之間的全部字符;
mypath="/etc/init.d/functions"
${mypath%/*}:/etc/init.d

url=http://www.china.org:80
    ${url##:*}
    ${url%%:*}

那麼接下來介紹的是如何進行查找及替換。

查找替換:
    ${var/PATTERN/SUBSTI}:查找var所表示的字符串中,第一次被PATTERN所匹配到的字符串,將其替換爲SUBSTI所表示的字符串;
    ${var//PATTERN/SUBSTI}:查找var所表示的字符串中,全部被PATTERN所匹配到的字符串,並將其所有其替換爲SUBSTI所表示的字符串;

    ${var/#PATTERN/SUBSTI}:查找var所表示的字符中,行首被PATTERN所匹配到的字符串,將其替換爲SUBSTI所表示的字符串;
    ${var/%PATTERN/SUBSTI}:查找var所表示的字符中,行尾被PATTERN所匹配到的字符串,將其替換爲SUBSTI所表示的字符串;

    注意:PATTERN中使用的glob風格和通配符;
# userinfo="root:x:0:0:root admin:/root:/bin/chroot"
# echo "${userinfo/root/ROOT}"
ROOT:x:0:0:root admin:/root:/bin/chroot
# echo "${userinfo//root/ROOT}"
ROOT:x:0:0:ROOT admin:/ROOT:/bin/chROOT
# echo "${userinfo/#r??t/ROOT}"
ROOT:x:0:0:root admin:/root:/bin/chroot
# echo "${userinfo/%r??t/ROOT}"
root:x:0:0:root admin:/root:/bin/chROOT

除了查找及替換外,還有一個就是查找及刪除。

${var/PATTERN}:以PATTERN爲模式查找var字符串中第一次匹配,並刪除之;
${var//PATTERN}:
${var/#PATTERN}:
${var/%PATTERN}:

那麼下一個介紹的就是字符大小寫轉換。

字符大小寫轉換:
    ${var^^}:把var中的全部小寫字符轉換爲大寫;
    ${var,,}:把var中的全部大寫字符轉換爲小寫;

最後一個介紹的是變量賦值。

變量賦值:
    ${var:-VALUE}:若是var變量爲空,或未設置,那麼返回VALUE;不然,則返回var變量的值;
    ${var:=VALUE}:若是var變量爲空,或未設置,那麼返回VALUE,並將VALUE賦值給var變量;不然,則返回var變量的值;
    ${var:+VALUE}:若是var變量爲空,則返回VALUE;
    ${var:?ERROR_INFO}:若是var爲空,或未設置,那麼返回ERROR_INFO爲錯誤提示;不然,返回var的值;

第一個就是若是這個變量自己有值,就返回本身自己的值,而若是這個變量沒值,就返回後面所定義的值。

# echo ${hi:-world}
world
# hi="america"
# echo ${hi:-world}
america

那麼第二個就是若是這個變量的值爲空的話,就將這個值賦予給這個變量,否則的話就返回這個變量的定義的值。

# echo $hello

# echo "${hello:=world}"
world
# echo $hello
world

第三個就是若是var不空,則返回VALUE

# unset hello
# echo $hello

# echo "${hello:+worlds}"

# hello=world
# echo "${hello:+worlds}"
worlds

以上就是bash內置的字符串處理工具,咱們如今示例如下腳本案例,來完成以下功能。

練習:寫一個腳本,完成以下功能:
    (1) 提示用戶輸入一個可執行命令的名稱;
    (2) 獲取命令所依賴的全部庫文件列表;
    (3) 複製命令至某個目標目錄(例如/mnt/sysroot)下的對應路徑中;
        bash, /bin/bash ==> /mnt/sysroot/bin/bash
    (4) 複製命令依賴到的全部庫文件至目標目錄下的所對應的路徑下;
        /lib64/ld-linux-x6464.so.2 ==> /mnt/sysroot/lib64/ld-linux-x8664.so.2

    進一步:
        每次複製完成一個命令後,不要退出,而是提示用戶繼續輸入要複製的其它命令,並重復完成如上所描述的功能;直到用戶輸入"quit"退出腳本;
寫一個腳本:
    ping命令去查看192.168.1.1-192.168.67.1範圍內的全部主機是否在線;在線顯示爲up,不在線顯示爲down,分別統計在線主機,及不在線的主機數;

    分別使用for, while, until循環實現;
#!/bin/bash
#
declare -i uphosts=0
declare -i downhosts=0

for i in {1..67}; do
    if ping -W1 -c1 192.168.$i.1 &> /dev/null;then
        echo "192.168.$i.1 is up."
        let uphosts+=1
    else
        echo "192.168.$i.1 is down."
        let downhosts+=1
    fi
done

echo "uphosts is $uphosts."
echo "downhosts is $downhosts."
#!/bin/bash
#
declare -i uphosts=0
declare -i downhosts=0
declare -i i=1

hostping() {
    if ping -W1 -c1 $1 &> /dev/null;then
        echo "$1 is up."
        return 0
    else
        echo "$1 is down."
        return 1
    fi
}

while [ $i -le 67 ]; do
    hostping 192.168.$i.1
    [ $? -eq 0 ] && let uphosts+=1 || let downhosts+=1
    let i++
done

echo "uphosts is $uphosts."
echo "downhosts is $downhosts."
相關文章
相關標籤/搜索