(八)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,,}

高级变量

(八)shell编程之数组,字符串还有高级变量

范例:

[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
上一篇:基于rsync daemon 实现 sersync


下一篇:centos8 防火墙