1. 程式人生 > 其它 >(八)shell程式設計之陣列,字串還有高階變數

(八)shell程式設計之陣列,字串還有高階變數

陣列 array

陣列介紹

變數:儲存單個元素的記憶體空間
陣列:儲存多個元素的連續的記憶體空間,相當於多個變數的集合
陣列名和索引
索引的編號從0開始,屬於數值索引
索引可支援使用自定義的格式,而不僅是數值格式,即為關聯索引,bash 4.0版本之後開始支援
bash的陣列支援稀疏格式(索引不連續)

宣告陣列

#普通陣列可以不事先宣告,直接使用
declare -a ARRAY_NAME
#關聯陣列必須先宣告,再使用
declare -A ARRAY_NAME

注意:兩者不可相互轉換

陣列賦值

陣列元素的賦值
(1) 一次只賦值一個元素

ARRAY_NAME[INDEX]=VALUE

範例:

weekdays[0]="Sunday"
weekdays[4]="Thursday"

(2) 一次賦值全部元素

ARRAY_NAME=("VAL1" "VAL2" "VAL3" ...)

範例:

title=("ceo" "coo" "cto")
num=({0..10})
alpha=({a..g})
file=( *.sh )

(3) 只賦值特定元素

ARRAY_NAME=([0]="VAL1" [3]="VAL2" ...)

(4) 互動式陣列值對賦值

read -a ARRAY

範例:

[root@centos8 ~]# declare -A course
[root@centos8 ~]# declare -a course
-bash: declare: course: cannot convert associative to indexed array
[root@centos8 ~]# file=( *.sh )
[root@centos8 ~]# declare -A file
-bash: declare: file: cannot convert indexed to associative array

範例:

[root@ubuntu1804 ~]# i=a
[root@ubuntu1804 ~]# j=1
[root@ubuntu1804 ~]# declare -A arr
[root@ubuntu1804 ~]# arr[$i$j]=long
[root@ubuntu1804 ~]# j=2
[root@ubuntu1804 ~]# arr[$i$j]=king
[root@ubuntu1804 ~]# echo ${arr[*]}
long king
[root@ubuntu1804 ~]# echo ${arr[a1]}
long
[root@ubuntu1804 ~]# echo ${arr[a2]}
king

顯示所有陣列

declare -a

範例:

[root@centos8 ~]# declare -a
declare -a BASH_ARGC=()
declare -a BASH_ARGV=()
declare -a BASH_COMPLETION_VERSINFO=([0]="2" [1]="7")
declare -a BASH_LINENO=()
declare -ar BASH_REMATCH=()
declare -a BASH_SOURCE=()
declare -ar BASH_VERSINFO=([0]="4" [1]="4" [2]="19" [3]="1" [4]="release"
[5]="x86_64-redhat-linux-gnu")
declare -a DIRSTACK=()
declare -a FUNCNAME
declare -a GROUPS=()
declare -a PIPESTATUS=([0]="0")

引用陣列

引用特定的陣列元素

${ARRAY_NAME[INDEX]}
#如果省略[INDEX]表示引用下標為0的元素

範例:

[root@centos8 ~]# declare -a title=([0]="ceo" [1]="coo" [2]="cto")
[root@centos8 ~]# echo ${title[1]}
coo
[root@centos8 ~]# echo ${title}
ceo
[root@centos8 ~]# echo ${title[2]}
cto
[root@centos8 ~]# echo ${title[3]}

引用陣列所有元素

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

範例:

[root@centos8 ~]# echo ${title[@]}
ceo coo cto
[root@centos8 ~]# echo ${title[*]}
ceo coo cto

陣列的長度,即陣列中元素的個數

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

範例:

[root@centos8 ~]# echo ${#title[*]}
3

刪除陣列

刪除陣列中的某元素,會導致稀疏格式

unset ARRAY[INDEX]

範例:

[root@centos8 ~]# echo ${title[*]}
ceo coo cto
[root@centos8 ~]# unset title[1]
[root@centos8 ~]# echo ${title[*]}
ceo cto

刪除整個陣列

unset ARRAY

範例:

[root@centos8 ~]# unset title
[root@centos8 ~]# echo ${title[*]}

陣列資料處理

陣列切片:

${ARRAY[@]:offset:number}
offset #要跳過的元素個數
number #要取出的元素個數
#取偏移量之後的所有元素
{ARRAY[@]:offset}

範例:

[root@centos8 ~]# num=({0..10})
[root@centos8 ~]# echo ${num[*]:2:3}
2 3 4
[root@centos8 ~]# echo ${num[*]:6}
6 7 8 9 10

向陣列中追加元素:

ARRAY[${#ARRAY[*]}]=value
ARRAY[${#ARRAY[@]}]=value

範例:

[root@centos8 ~]# num[${#num[@]}]=11
[root@centos8 ~]# echo ${#num[@]}
12
[root@centos8 ~]# echo ${num[@]}
0 1 2 3 4 5 6 7 8 9 10 11

關聯陣列

declare -A ARRAY_NAME
ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2‘...)

注意:關聯陣列必須先宣告再呼叫

範例

[root@centos8 ~]# name[ceo]=long
[root@centos8 ~]# name[cto]=wang
[root@centos8 ~]# name[coo]=zhang
[root@centos8 ~]# echo ${name[ceo]}
zhang
[root@centos8 ~]# echo ${name[cto]}
zhang
[root@centos8 ~]# echo ${name[coo]}
zhang
[root@centos8 ~]# echo ${name}
zhang
[root@centos8 ~]# declare -A name
-bash: declare: name: cannot convert indexed to associative array
[root@centos8 ~]# unset name
[root@centos8 ~]# declare -A name
[root@centos8 ~]# name[ceo]=long
[root@centos8 ~]# name[cto]=wang
[root@centos8 ~]# name[coo]=zhang
[root@centos8 ~]# echo ${name[coo]}
zhang
[root@centos8 ~]# echo ${name[ceo]}
long
[root@centos8 ~]# echo ${name[cto]}
wang
[root@centos8 ~]# echo ${name[*]}
long wang zhang

範例:關聯陣列

[root@centos8 ~]# declare -A student
[root@centos8 ~]# student[name1]=lijun
[root@centos8 ~]# student[name2]=ziqing
[root@centos8 ~]# student[age1]=18
[root@centos8 ~]# student[age2]=16
[root@centos8 ~]# student[gender1]=m
[root@centos8 ~]# student[city1]=nanjing
[root@centos8 ~]# student[gender2]=f
[root@centos8 ~]# student[city2]=anhui
[root@centos8 ~]# student[gender2]=m
[root@centos8 ~]# student[name50]=alice
[root@centos8 ~]# student[name3]=tom
[root@centos8 ~]# for i in {1..50};do echo student[name$i]=${student[name$i]};done

範例:編寫指令碼,定義一個數組,陣列中的元素對應的值是/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"

字串處理

基於偏移量取字串

#返回字串變數var的字元的長度,一個漢字算一個字元
${#var}
#返回字串變數var中從第offset個字元後(不包括第offset個字元)的字元開始,到最後的部分,
offset的取值在0 到 ${#var}-1 之間(bash4.2後,允許為負值)
${var:offset}
#返回字串變數var中從第offset個字元後(不包括第offset個字元)的字元開始,長度為number的部分
${var:offset:number}
#取字串的最右側幾個字元,取字串的最右側幾個字元, 注意:冒號後必須有一空白字元
${var: -length}
#從最左側跳過offset字元,一直向右取到距離最右側lengh個字元之前的內容,即:掐頭去尾
${var:offset:-length}
#先從最右側向左取到length個字元開始,再向右取到距離最右側offset個字元之間的內容,注意:-
length前空格,並且length必須大於offset
${var: -length:-offset}

範例:

[root@centos8 script40]# str=abcdef我你他
[root@centos8 script40]# echo ${#str}
9
[root@centos8 script40]# echo ${str:2}
cdef我你他
[root@centos8 script40]# echo ${str:2:3}
cde
[root@centos8 script40]# echo ${str:-3}
abcdef我你他
[root@centos8 script40]# echo ${str: -3}
我你他
[root@centos8 script40]# echo ${str:2:-3}
cdef
[root@centos8 script40]# echo ${str: -2:-3}
-bash: -3: substring expression < 0
[root@centos8 script40]# echo ${str: -3:-2}
我
[root@centos8 script40]# echo ${str:-3:-2}
abcdef我你他
[root@centos8 script40]# echo ${str: -3:-2}
我
[root@centos8 script40]# echo ${str: -5:-2}
ef我

基於模式取子串

#其中word可以是指定的任意字元,自左而右,查詢var變數所儲存的字串中,第一次出現的word, 刪除字
符串開頭至第一次出現word字串(含)之間的所有字元,即懶惰模式,以第一個word為界刪左留右
${var#*word}
#同上,貪婪模式,不同的是,刪除的是字串開頭至最後一次由word指定的字元之間的所有內容,即貪婪模
式,以最後一個word為界刪左留右
${var##*word}

範例:

[root@centos8 ~]# file="var/log/messages"
[root@centos8 ~]# echo ${file#*/}
log/messages
[root@centos8 ~]# echo ${file##*/}
messages
#其中word可以是指定的任意字元,功能:自右而左,查詢var變數所儲存的字串中,第一次出現的word,
刪除字串最後一個字元向左至第一次出現word字串(含)之間的所有字元,即懶惰模式,以從右向左的第
一個word為界刪右留左
${var%word*}
#同上,只不過刪除字串最右側的字元向左至最後一次出現word字元之間的所有字元,即貪婪模式,以從右向
左的最後一個word為界刪右留左
${var%%word*}

範例:

[root@centos8 ~]# file="var/log/messages"
[root@centos8 ~]# echo ${file%/*}
var/log
[root@centos8 ~]# echo ${file%%/*}
var

範例:

[root@centos8 ~]# url=http://www.longxuan.com:8080
[root@centos8 ~]# echo ${url##*:}
8080
[root@centos8 ~]# echo ${url%%:*}
http

查詢替換

#查詢var所表示的字串中,第一次被pattern所匹配到的字串,以substr替換之
${var/pattern/substr}
#查詢var所表示的字串中,所有能被pattern所匹配到的字串,以substr替換之
${var//pattern/substr}
#查詢var所表示的字串中,行首被pattern所匹配到的字串,以substr替換之
${var/#pattern/substr}
#查詢var所表示的字串中,行尾被pattern所匹配到的字串,以substr替換之
${var/%pattern/substr}

查詢並刪除

#刪除var表示的字串中第一次被pattern匹配到的字串
${var/pattern}
刪除var表示的字串中所有被pattern匹配到的字串
${var//pattern}
刪除var表示的字串中所有以pattern為行首匹配到的字串
${var/#pattern}
刪除var所表示的字串中所有以pattern為行尾所匹配到的字串
${var/%pattern}

字元大小寫轉換

#把var中的所有小寫字母轉換為大寫
${var^^}
#把var中的所有大寫字母轉換為小寫
${var,,}

高階變數

範例:

[root@centos8 ~]# title=ceo
[root@centos8 ~]# name=${title-long}
[root@centos8 ~]# echo $name
ceo
[root@centos8 ~]# title=
[root@centos8 ~]# name=${title-long}
[root@centos8 ~]# echo $name
[root@centos8 ~]# unset title
[root@centos8 ~]# name=${title-long}
[root@centos8 ~]# echo $name
long

範例:

[root@centos8 ~]# title=ceo
[root@centos8 ~]# name=${title:-long}
[root@centos8 ~]# echo $name
ceo
[root@centos8 ~]# title=
[root@centos8 ~]# name=${title:-long}
[root@centos8 ~]# echo $name
long
[root@centos8 ~]# unset title
[root@centos8 ~]# name=${title:-long}
[root@centos8 ~]# echo $name
long

高階變數用法-有型別變數

Shell變數一般是無型別的,但是bash Shell提供了declare和typeset兩個命令用於指定變數的型別,兩
個命令是等價的

declare [選項] 變數名
選項:
-r 宣告或顯示只讀變數
-i 將變數定義為整型數
-a 將變數定義為陣列
-A 將變數定義為關聯陣列
-f 顯示已定義的所有函式名及其內容
-F 僅顯示已定義的所有函式名
-x 宣告或顯示環境變數和函式,相當於export
-l 宣告變數為小寫字母 declare -l var=UPPER
-u 宣告變數為大寫字母 declare -u var=lower
-n make NAME a reference to the variable named by its value

變數間接引用

eval命令

eval命令將會首先掃描命令列進行所有的置換,然後再執行該命令。該命令適用於那些一次掃描無法實
現其功能的變數,該命令對變數進行兩次掃描
範例:

[root@centos8 ~]# CMD=whoami
[root@centos8 ~]# echo $CMD
whoami
[root@centos8 ~]# eval $CMD
root
[root@centos8 ~]# n=10
[root@centos8 ~]# echo {0..$n}
{0..10}
[root@centos8 ~]# eval echo {0..$n}
0 1 2 3 4 5 6 7 8 9 10
[root@centos8 ~]# for i in `eval echo {1..$n}` ;do echo i=$i ;done
i=1
i=2
i=3
i=4
i=5
i=6
i=7
i=8
i=9
i=10

[root@centos8 ~]# i=a
[root@centos8 ~]# j=1
[root@centos8 ~]# $i$j=hello
-bash: a1=hello: command not found
[root@centos8 ~]# eval $i$j=hello
[root@centos8 ~]# echo $i$j
a1
[root@centos8 ~]# echo $a1
hello

間接變數引用

如果第一個變數的值是第二個變數的名字,從第一個變數引用第二個變數的值就稱為間接變數引用
variable1的值是variable2,而variable2又是變數名,variable2的值為value,間接變數引用是指通過
variable1獲得變數值value的行為

variable1=variable2
variable2=value
#示例:
i=1
$1=wang

bash Shell提供了兩種格式實現間接變數引用

#方法1
#變數賦值
eval tempvar=\$$variable1
#顯示值
eval echo \$$variable1
eval echo '$'$variable1
#方法2
#變數賦值
tempvar=${!variable1}
#顯示值
echo ${!variable1}

範例:

[root@centos8 ~]# ceo=name
[root@centos8 ~]# name=long
[root@centos8 ~]# echo $ceo
name
[root@centos8 ~]# echo $$ceo
33722ceo
[root@centos8 ~]# echo $$
33722
[root@centos8 ~]# echo \$$ceo
$name
[root@centos8 ~]# eval echo \$$ceo
long
[root@centos8 ~]# eval tmp=\$$ceo
[root@centos8 ~]# echo $tmp
long
[root@centos8 ~]# echo ${!ceo}
long

範例:

[root@server ~]# N1=N2
[root@server ~]# N2=longwang
[root@server ~]# eval NAME=\$$N1
[root@server ~]# echo $NAME
longwang
[root@server ~]# NAME=${!N1}
[root@server ~]# echo $NAME
longwang

範例: 批量建立使用者

#!/bin/bash
n=$#
[ $n -eq 0 ] && { echo "Usage: `basename $0` username..." ; exit 2; }
for i in `eval echo {1..$n}`;do
  user=${!i}
  id $user &> /dev/null && echo $user is exist || { useradd $user; echo $user
is created; }
done

[root@centos8 ~]# bash create_user.sh hehe xixi haha
hehe is created
xixi is created
haha is created

變數引用 reference

[root@centos8 ~]#cat test.sh
#!/bin/bash
title=ceo
ceo=long
#宣告引用變數ref
declare -n ref=$title
[ -R ref ] && echo "reference" # [ -R var ] 是bash4.4新特性
echo ref=$ref
ceo=king
echo ref=$ref
[root@centos8 ~]# bash test.sh
reference
ref=long
ref=king