bashl脚本编程之数组和字符串处理

===============================================================================

概述:


===============================================================================

数组

 1.数组的定义及声明

定义:

变量:

  • 存储单个元素的内存空间;

数组:

  • 存储多个元素的连续的内存空间,相当于多个变量的集合;

数组名和索引:

  • 数组名:整个数组只有一个名字;

  • 索引:编号从0开始,属于数值索引;

  • bash的数组支持稀疏格式(索引不连续)

格式:

  • 数组名[索引]

  • ${ARRAY_NAME[INDEX]}  必需加花括号 { }

注意:

  • 索引可支持使用自定义的索引格式,而不仅是数值格式(0,1,2...),即为关联数组,bash4.0版本之后开始支持。

声明数组:

  • declare -a ARRAY_NAME:声明一个索引数组

  • declare -A ARRAY_NAME:声明一个关联数组 (必须先声明再使用)

版本查询

[root@centos7 ~]# bash --version
GNU bash, 版本 4.2.46(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
许可证 GPLv3+: GNU GPL 许可证版本3或者更高 <http://gnu.org/licenses/gpl.html>

这是自由软件,您可以自由地更改和重新发布。
在法律允许的范围内没有担保.

 2.数组元素的赋值

一次只赋值一个元素:

格式:

  • ARRAY_NAME[INDEX]=VALUE

示例:

      weekdays[0]="Sunday"

      weekdays[4]="Thursday"

一次只赋值全部元素:

格式:

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

 注意:可以使用 命令引用,glob 通配等方法赋值

一次只赋值特定元素

格式:

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

  • 支持稀疏格式的数组

使用read 交互式数组赋值

格式:

  • read -a ARRAY

演示:

   1.一次只赋值一个元素:

[root@centos7 ~]# declare -a animals  # 使用前最好先声明数组,当然不声明也可以

# 一次赋值一个元素
[root@centos7 ~]# animals[0]=pig
[root@centos7 ~]# animals[1]=dog

# 查看赋值,一定要注意书写格式,一定要加 {}
[root@centos7 ~]# echo $animals  
pig
[root@centos7 ~]# echo $animals[0]  # 错误写法,把其看做两部分 $animals 替换为 pig,[0]随后
pig[0]
[root@centos7 ~]# echo ${animals[0]}
pig
[root@centos7 ~]# echo ${animals[1]}
dog
[root@centos7 ~]# echo ${animals[*]}  # 查看数组所有赋值
pig dog

 2.一次只赋值全部元素:

[root@centos7 ~]# number=(1 3 5 6 8 9)
[root@centos7 ~]# echo ${number[3]}
6
[root@centos7 ~]# echo ${number[0]}
1
[root@centos7 ~]# echo ${number[5]}
9
[root@centos7 ~]# echo ${number[*]}
1 3 5 6 8 9

# 下面的赋值方法也可以
[root@centos7 ~]# number=({1..9})
[root@centos7 ~]# echo ${number[*]}
1 2 3 4 5 6 7 8 9

[root@centos7 ~]# files=(/home/*)
[root@centos7 ~]# echo ${files[*]}
/home/centos /home/mage /home/mageedu

[root@centos7 ~]# files=($(ls /home))  # 命令引用 $()
[root@centos7 ~]# echo ${files[*]}
centos mage mageedu

[root@centos7 ~]# files=({1,2}{a,b})  # 命令行展开
[root@centos7 ~]# echo ${files[*]}
1a 1b 2a 2b

3.只赋值特定元素,稀疏格式的数组

[root@centos7 ~]# num=([0]=100 [2]=50 [3]=20)
[root@centos7 ~]# echo ${num[0]}
100
[root@centos7 ~]# echo ${num[2]}
50
[root@centos7 ~]# echo ${num[1]}

[root@centos7 ~]# echo ${num[3]}
20
[root@centos7 ~]# echo ${num[*]}
100 50 20

4.使用read交互式数组赋值

[root@centos7 ~]# read -a num
1 22 44 55 66 77 88 99
[root@centos7 ~]# echo ${num[0]}
1
[root@centos7 ~]# echo ${num[4]}
66
[root@centos7 ~]# echo ${num[6]}
88
[root@centos7 ~]# echo ${num[*]}
1 22 44 55 66 77 88 99

5.关联数组赋值

# 使用自定义的索引格式,即关联数组,如果不事先声明,会出现如下错误,索引总是显示最后一个元素
[root@centos7 ~]# username[F]=tao
[root@centos7 ~]# username[M]=xiu
[root@centos7 ~]# username[J]=jing
[root@centos7 ~]# echo ${username[F]}
jing
[root@centos7 ~]# echo ${username[M]}
jing
[root@centos7 ~]# echo ${username[J]}
jing
[root@centos7 ~]# echo ${username[*]}
jing

# 已经为索引数组就不能再次声明为关联数组了
[root@centos7 ~]# declare -A username  
-bash: declare: username: 无法将索引数组转化为关联数组

# 声明一个关联数组,并赋值
[root@centos7 ~]# declare -A jianghu
[root@centos7 ~]# jianghu[A]=linghuchong
[root@centos7 ~]# jianghu[D]=qiaofeng
[root@centos7 ~]# jianghu[G]=yangguo

[root@centos7 ~]# echo "${jianghu[A]}"
linghuchong
[root@centos7 ~]# echo ${jianghu[D]}
qiaofeng
[root@centos7 ~]# echo ${jianghu[G]}
yangguo
[root@centos7 ~]# echo ${jianghu[*]}
linghuchong qiaofeng yangguo

 3.引用数组元素

引用数组中的元素:

格式:

  • ${ARRAY_NAME[INDEX]}

注意:

  • 省略[INDEX]表示引用下标为0的元素;

  • 索引数组是从0开始缩印的,如果数组元素个数为n,那么索引数为 n-1

数组的长度(数组中元素的个数):

  • ${#ARRAY_NAME[*]}

  • ${#ARRAY_NAME[@]}

引用数组中的所有元素

  • ${ARRAY_NAME[*]}

  • ${ARRAY_NAME[@]}

演示:  

[root@centos7 ~]# num=({1..9})
[root@centos7 ~]# echo ${#num[*]}
9

[root@centos7 ~]# echo ${num[*]}
1 2 3 4 5 6 7 8 9
[root@centos7 ~]# echo ${num[@]}
1 2 3 4 5 6 7 8 9
[root@centos7 ~]# echo ${num[*]}
1 2 3 4 5 6 7 8 9

[root@centos7 ~]# var=(taotao jing xiu)
[root@centos7 ~]# echo ${#var[*]}
3
[root@centos7 ~]# echo ${var[*]}
taotao jing xiu

# {}里面是一个变量,${#VAR}就表示变量的长度,而之前的 $#,表示传递给脚本的参数个数,注意区别
[root@centos7 ~]# echo ${#var}  
6
[root@centos7 ~]# echo ${#var[2]}
3
[root@centos7 ~]# echo ${#var[1]}
4

 4.数组元素切片

格式:

  ${ARRAY_NAME[@]:offest:number}

  • offest:要跳过的元素个数,即偏移量;

  • number:要取出的元素个数;省略number时,表示取偏移量之后的所有元素;

演示:

[root@centos7 ~]# ls -d /etc/[Pp]*
/etc/pam.d    /etc/php.d         /etc/php.ini  /etc/pki       /etc/polkit-1  /etc/ppp             /etc/profile    /etc/pulse
/etc/passwd   /etc/php-fpm.conf  /etc/pinforc  /etc/plymouth  /etc/popt.d    /etc/prelink.conf.d  /etc/profile.d  /etc/python
/etc/passwd-  /etc/php-fpm.d     /etc/pkcs11   /etc/pm        /etc/postfix   /etc/printcap        /etc/protocols

# 一次赋值数组全部元素
[root@centos7 ~]# files=(/etc/[Pp]*)

[root@centos7 ~]# echo ${files[*]}
/etc/pam.d /etc/passwd /etc/passwd- /etc/php.d /etc/php-fpm.conf /etc/php-fpm.d 
/etc/php.ini /etc/pinforc /etc/pkcs11 /etc/pki /etc/plymouth /etc/pm /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/python

[root@centos7 ~]# echo ${#files[*]}
23

# 跳过前两个元素,取后面的三个元素
[root@centos7 ~]# echo ${files[@]:2:3}
/etc/passwd- /etc/php.d /etc/php-fpm.conf

# 跳过前20个元素,取后面的所有元素
[root@centos7 ~]# echo ${files[@]:20}
/etc/protocols /etc/pulse /etc/python

 5.数组的其他用法

向非稀疏格式数组中追加元素

  • ARRAY_NAEM[${#ARRAY_NAME[*]}]=

删除数组中的某元素

  • unset ARRAY[INDEX]

关联数组

首先一定要声明数组

  • declare -A ARRAY_NAME

赋值

  • ARRAY_NAME=([index_name1]="value1" [index_name2]="value2"...)



练习:

  1.生成10个随机数保存于数组中,并找出其最大值和最小值

[root@centos7 bin]# cat shuzu.sh
#!/bin/bash

declare -a rand
declare -i max=0
declare -i min=0

#思路:刚生成的第一个数同时赋值给最大和最小值,然后和后面生成的随机数作比较,如果大于最大值
# 就赋值给 max,如果小于 min 就赋值给 min

for i in {0..9};do
	rand[$i]=$RANDOM
	echo ${rand[$i]}
	if [ $i -eq 0 ];then
    	max=${rand[$i]}
		min=${rand[$i]}
        elif [ ${rand[$i]} -gt $max ];then
		max=${rand[$i]}
        elif [ ${rand[$i]} -lt $min ];then
		min=${rand[$i]}
	fi
done

echo "MAX=$max"
echo "MIN=$min"

#==========================================================
[root@centos7 bin]# bash shuzu.sh
31054
4869
13722
9142
8027
16340
11938
10497
11232
13826
MAX=31054
MIN=4869

# 执行过程如下:
[root@centos7 bin]# bash -x shuzu.sh
+ declare -a rand
+ declare -i max=0
+ declare -i min=0
+ for i in '{0..9}'
+ rand[$i]=31289
+ echo 31289
31289
+ '[' 0 -eq 0 ']'
+ max=31289
+ min=31289
+ for i in '{0..9}'
+ rand[$i]=21973
+ echo 21973
21973
+ '[' 1 -eq 0 ']'
+ '[' 21973 -gt 31289 ']'
+ '[' 21973 -lt 31289 ']'
+ min=21973

 2.写一个脚本,定义一个数组,数组中的元素是/var/log目录下所有以.log结尾的文件;要统计其下标为偶数的文件中的行数之和。

#!/bin/bash
#

declare -a files

files=(/var/log/*.log)

for i in $(seq 0 $[${#files[*]}-1]);do
	if [ $[$i%2] -eq 0 ];then
		lines=$(wc -l ${files[$i]} |cut -d" " -f1)  # 取文件行数
		sumlines=$[$sumlines+$lines]                # 总行数
	fi
done

echo "行数之和为:$sumlines"

bash的内置字符串处理工具

 1.字符串切片

用法:

${#var}:

  • 返回字符串变量var的长度;

${var:offset}:

  • 返回字符串变量var中从第offset个字符后(不包括第offset个字符)的字符开始,到最后的部分,offset的取值在0 到${#var}-1 之间;

${var:offset:number}:

  • 返回字符串变量var中从第offset个字符后(不包括第offset个字符)的字符开始,长度为number的部分;

${var: -lengh}:

  • 取字符串的最右侧几个字符:注意:冒号后必须有一空白字符

演示:

[root@centos7 ~]# name=guojing
[root@centos7 ~]# echo ${name}
guojing

# 查看字符串变量的长度
[root@centos7 ~]# echo ${#name}
7

# 跳过指定的偏移量,取后面的所有字符
[root@centos7 ~]# echo ${name:2}
ojing
[root@centos7 ~]# echo ${name:3}
jing

# 跳过指定的偏移量,取指定长度的字符
[root@centos7 ~]# echo ${name:2:3}
oji
[root@centos7 ~]# echo ${name:3:3}
jin

#取字符串最右侧的指定长度字符
[root@centos7 ~]# echo ${name: -4}
jing
[root@centos7 ~]# echo ${name: -3}
ing

 2.基于模式取子串

用法:

${var#*word}:其中word可以是指定的任意字符

功能:

  • 左而右,查找var变量所存储的字符串中,第一次出现的word, 删除字符串开头至第一次出现word字符之间的所有字符,包括word;

${var##*word}:

功能:

  • 同上,不同的是,删除的是字符串开头至最后一次由word指定的字符之间的所有内容;

${var%word*}:

功能:

  • 自右而左,查找var变量所存储的字符串中,第一次出现的word, 删除字符串最后一个字符向左至第一次出现word字符之间的所有字符;

${var%%word*}:

功能:

  • 同上,只不过删除字符串最右侧的字符向左至最后一次出现word字符之间的所有字符;

演示:

[root@centos7 ~]# mypath="/etc/init.d/functions"

# 自左而右查找由 word 指定的任意字符,并删除
[root@centos7 ~]# echo ${mypath}
/etc/init.d/functions

[root@centos7 ~]# echo ${mypath#*/}
etc/init.d/functions

[root@centos7 ~]# echo ${mypath##*/}
functions

#==================================================================

# 自右而左查找由 word 指定的任意字符,并删除
[root@centos7 ~]# echo ${mypath%/*}
/etc/init.d

[root@centos7 ~]# echo ${mypath%%/*} # 因为最有一个为 路径首部的 / 所以全都删除了


[root@centos7 ~]# url=http://www.magedu.com:80

[root@centos7 ~]# echo ${url##*/} # 自左而右查找最后一次出现“/”的字符,并删除之间所有的
www.magedu.com:80

[root@centos7 ~]# echo ${url%%:*} # 自右而左查找最后一次出现“:”的字符,并删除之间所有的
http

[root@centos7 ~]# echo ${url##*:} # 自左而右查找最后一次出现“:”的字符,并删除之间所有的,实为取端口号
80

 3.字符串查找替换

用法:

${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 风格的通配符

演示:

[root@centos7 ~]# userinfo="root:x:0:0:root admin:/root:/bin/chroot"

[root@centos7 ~]# echo ${userinfo/root/ROOT}
ROOT:x:0:0:root admin:/root:/bin/chroot

[root@centos7 ~]# echo ${userinfo/r..t/ROOT} # 不支持使用正则表达式的模式匹配
root:x:0:0:root admin:/root:/bin/chroot

root@centos7 ~]# echo ${userinfo/r??t/ROOT}  # 支持 glob 风格的模式匹配
ROOT:x:0:0:root admin:/root:/bin/chroot

[root@centos7 ~]# echo ${userinfo//root/ROOT}  #查找所有能够被root匹配到的字符串,并替换为ROOT
ROOT:x:0:0:ROOT admin:/ROOT:/bin/chROOT

[root@centos7 ~]# echo ${userinfo//r??t/ROOT}
ROOT:x:0:0:ROOT admin:/ROOT:/bin/chROOT

[root@centos7 ~]# echo ${userinfo/#r??t/ROOT} # 查找行首能够被r??t匹配到的字符串,并替换为ROOT
ROOT:x:0:0:root admin:/root:/bin/chroot

[root@centos7 ~]# echo ${userinfo/%r??t/ROOT} # 查找行尾能够被r??t匹配到的字符串,并替换为ROOT
root:x:0:0:root admin:/root:/bin/chROOT

 4.字符串查找并删除

用法:

${var/pattern}:

  • 查找var所表示的字符串中,删除第一次被pattern所匹配到的字符串;

${var//pattern}:

  • 查找var所表示的字符串中,删除所有被pattern所匹配到的字符串;

${var/#pattern}:

  • 查找var所表示的字符串中,删除行首被pattern所匹配到的字符串;

${var/%pattern}:

  • 查找var所表示的字符串中,删除行尾被pattern所匹配到的字符串;

演示:

[root@centos7 ~]# echo ${userinfo}
root:x:0:0:root admin:/root:/bin/chroot

[root@centos7 ~]# echo ${userinfo/r??t} # 删除第一次被pattern所匹配到的字符串
:x:0:0:root admin:/root:/bin/chroot

[root@centos7 ~]# echo ${userinfo//r??t} # 删除所有被pattern所匹配到的字符串;
:x:0:0: admin:/:/bin/ch 

[root@centos7 ~]# echo ${userinfo/#r??t} # 删除行首被pattern所匹配到的字符串;
:x:0:0:root admin:/root:/bin/chroot

[root@centos7 ~]# echo ${userinfo/%r??t} # 删除行尾被pattern所匹配到的字符串;
root:x:0:0:root admin:/root:/bin/ch

 5.字符大小写转换

用法:

  • ${var^^}:把var中的所有小写字母转换为大写

  • ${var,,}:把var中的所有大写字母转换为小写

演示:

[root@centos7 ~]# echo $url
http://www.magedu.com:80

[root@centos7 ~]# echo ${url^^}  # 小写转换为大写
HTTP://WWW.MAGEDU.COM:80

[root@centos7 ~]# myurl=${url^^}

[root@centos7 ~]# echo $myurl
HTTP://WWW.MAGEDU.COM:80

[root@centos7 ~]# echo ${myurl,,}  # 大写转换为小写
http://www.magedu.com:80

 6.变量赋值

用法:

  • ${var:-value}:如果var为空或未设置,那么返回value;否则,则返回var的值;

  • ${var:=value}:如果var为空或未设置,那么返回value,并将value赋值给var;否则,则返回var的值;

  • ${var:+value}:如果var不空,则返回value;

  • ${var:?error_info}:如果var为空或未设置,那么返回error_info(错误信息提示);否则,则返回var的值;

演示:

[root@centos7 ~]# echo $hi  # 变量 hi 为空

[root@centos7 ~]# echo ${hi:-world} # 如果为空或未设置,那么返回word
world
[root@centos7 ~]# echo ${hi}  # 但是并没有赋值给变量 hi

[root@centos7 ~]# hi=china   # 现在赋值给变量 hi
[root@centos7 ~]# echo ${hi:-world}  # 有值,则返回hi的变量值
china 
[root@centos7 ~]# unset hi  # 撤销变量
[root@centos7 ~]# echo ${hi} 

#==========================================================
[root@centos7 ~]# echo ${hi:=world} #为空或未设置,那么返回world,并将world赋值给hi
world
[root@centos7 ~]# echo ${hi}
world