Shell 数组实践
Shell 数组介绍
为什么会产生 Shell 数组
- 开发 Shell 脚本时,定义变量通常采用
var=value的形式。若有多个变量要定义,逐个定义会很麻烦:
bash
a=1
b=2
c=3
- 快速读取不同变量的值也是一件很痛苦的事情。
什么是 Shell 数组
简单地说,Shell 数组就是一个元素集合:把有限个元素(变量或字符内容)用一个名字命名,用编号区分。这个名字称为数组名 ,编号称为数组下标 ,各个元素称为数组元素(也称下标变量)。
有了 Shell 数组之后,就可以用相同名字引用一系列变量及变量值,并通过数字(索引)识别使用它们。在很多场合中,使用数组可以缩短和简化程序开发。
Shell 数组的基本操作
Shell 数组的定义
Shell 数组的定义有多种方法,列举如下。
方法 1:括号赋值(推荐)
用小括号将变量值括起来赋值给数组变量,每个变量值之间用空格分隔。
语法:
bash
array=(value1 value2 value3 ...)
此为常用定义方法,需要重点掌握。
示例:
bash
# 用小括号将数组内容赋值给数组变量,数组元素用空格分隔
[rich@rich-shell ~]$ array=(1 2 3)
# 输出数组所有元素值
[rich@rich-shell ~]$ echo ${array[*]}
1 2 3
# 数组变量下标从 0 开始
[rich@rich-shell ~]$ echo ${array[0]}
1
方法 2:键值对赋值(不推荐)
用小括号括起来,同时采用键值对形式赋值。
语法:
bash
array=([1]=one [2]=two [3]=three)
小括号里对应的数字为数组下标,等号后面为下标对应的值。此方法较复杂,不推荐使用。
示例:
bash
[rich@rich-shell ~]$ array=([0]=one [1]=two [2]=three)
[rich@rich-shell ~]$ echo ${array[*]}
one two three
[rich@rich-shell ~]$ echo ${array[0]}
one
[rich@rich-shell ~]$ echo ${array[1]}
two
方法 3:逐元素赋值(不推荐)
通过分别定义数组变量的方法来定义。
语法:
bash
array[0]=a
array[1]=b
array[2]=c
此种定义方法比较麻烦,不推荐使用。
示例:
bash
[rich@rich-shell ~]$ array[0]=a
[rich@rich-shell ~]$ array[1]=b
[rich@rich-shell ~]$ array[2]=c
[rich@rich-shell ~]$ echo ${array[0]}
a
方法 4:命令输出作为数组内容
动态定义数组变量,使用命令的输出结果作为数组内容。
语法:
bash
array=($(命令))
# 或者
array=(`命令`)
示例:
bash
[rich@rich-shell ~]$ sudo mkdir /array
[rich@rich-shell ~]$ sudo touch /tmp/file-{1..3}.txt
[rich@rich-shell ~]$ array=($(ls /tmp/file*.txt))
[rich@rich-shell ~]$ echo ${array[*]}
/tmp/file-1.txt /tmp/file-2.txt /tmp/file-3.txt
Shell 数组其他操作
打印数组变量值
bash
[rich@rich-shell ~]$ array=(a b c d)
# 打印数组第一个元素值
[rich@rich-shell ~]$ echo ${array[0]}
a
# 打印数组所有元素
[rich@rich-shell ~]$ echo ${array[*]}
a b c d
[rich@rich-shell ~]$ echo ${array[@]}
a b c d
${array[*]}与${array[@]}在双引号内行为不同;不加引号时效果通常等同。
打印数组元素的个数
bash
# 以下两种方式效果等同
[rich@rich-shell ~]$ echo ${#array[*]}
4
[rich@rich-shell ~]$ echo ${#array[@]}
4
竖向定义数组(多行更清晰):
bash
[rich@rich-shell ~]$ array=(
linux
mysql
httpd
python
)
数组赋值
可直接通过「数组名[下标]」对数组引用赋值:下标不存在则自动添加新元素;下标存在则覆盖原值。
bash
# 下标不存在,添加新元素
[rich@rich-shell ~]$ array[5]=6
[rich@rich-shell ~]$ echo ${array[*]}
1 b c d 6
# 下标 4 的第 5 个元素未定义
[rich@rich-shell ~]$ echo ${array[4]}
[rich@rich-shell ~]$ echo ${array[5]}
6
# 下标存在,覆盖原值
[rich@rich-shell ~]$ array[0]=1
数组的删除
数组本质上仍是变量,可通过 unset 清除元素或整个数组。
bash
[rich@rich-shell ~]$ echo ${array[*]}
1 b c d
# 删除整个数组
[rich@rich-shell ~]$ unset array
[rich@rich-shell ~]$ echo ${array[*]}
# 删除指定下标元素
[rich@rich-shell ~]$ unset array[5]
[rich@rich-shell ~]$ echo ${array[5]}
数组内容截取
与变量子串的替换提取相同,因为数组是特殊的变量。
bash
[rich@rich-shell ~]$ array=(a b c d e)
# 截取下标 1--3 的元素
[rich@rich-shell ~]$ echo ${array[@]:1:3}
b c d
# 截取下标 1 之后的所有元素
[rich@rich-shell ~]$ echo ${array[@]:1}
b c d e
数组内容替换
与变量子串替换相同。
bash
[rich@rich-shell ~]$ array=({a..z})
[rich@rich-shell ~]$ echo ${array[@]}
a b c d e f g h i j k l m n o p q r s t u v w x y z
# 替换展示,数组内容并未改变
[rich@rich-shell ~]$ echo ${array[@]/b/2}
a 2 c d e f g h i j k l m n o p q r s t u v w x y z
[rich@rich-shell ~]$ echo ${array[@]}
a b c d e f g h i j k l m n o p q r s t u v w x y z
# 直接修改数组内容
[rich@rich-shell ~]$ array=(${array[@]/b/2})
[rich@rich-shell ~]$ echo ${array[@]}
a 2 c d e f g h i j k l m n o p q r s t u v w x y z
数组内容删除(模式删除)
与变量子串删除相同,对每个元素分别生效。
bash
[rich@rich-shell ~]$ array=(abc123abc123 abcd1234abcd1234)
[rich@rich-shell ~]$ echo ${array[@]}
abc123abc123 abcd1234abcd1234
# 针对每个元素从左删除
[rich@rich-shell ~]$ echo ${array[@]#*c}
123abc123 d1234abcd1234
[rich@rich-shell ~]$ echo ${array[@]##*c}
123 d1234
# 针对每个元素从右删除
[rich@rich-shell ~]$ echo ${array[@]%c*}
abc123ab abcd1234ab
[rich@rich-shell ~]$ echo ${array[@]%%c*}
ab ab
Shell 数组脚本开发实践
示例 1:通过循环打印数组所有元素
C 语言型 for 循环:
bash
# array1.sh
#!/bin/bash
array=({a..d})
for (( i=0; i<${#array[@]}; i++ )); do
echo ${array[i]}
done
执行结果:
text
[rich@rich-shell ~]$ bash array1.sh
a
b
c
d
普通 for 循环:
bash
# array2.sh
#!/bin/bash
array=({a..d})
for str in ${array[@]}; do
echo $str
done
while 循环:
bash
# array3.sh
#!/bin/bash
array=({a..d})
i=0
while (( i<${#array[@]} )); do
echo ${array[i]}
((i++))
done
例 1:打印字母数不大于 6 的单词
题目句子:
text
I am linux teacher welcome to wanho training classroom
脚本 print_words.sh:
bash
#!/bin/bash
strings=(I am linux teacher welcome to wanho training classroom)
i=0
while (( i<${#strings[@]} )); do
if [ ${#strings[i]} -le 6 ]; then
echo ${strings[i]}
fi
((i++))
done
执行结果:
text
[rich@rich-shell ~]$ bash print_words.sh
I
am
linux
to
wanho
例 2:批量检查多个网站地址是否正常
使用 Shell 数组实现,检测策略尽量模拟用户访问。每 10 秒进行一次全部检测,无法访问时输出报警。
待检测地址示例:
脚本 check_web.sh:
bash
#!/bin/bash
url_list=(
http://www.baidu.com
http://www.redhat.fun
http://www.redhat.com
)
function check_url() {
echo "==========检查站点是否可以访问=========="
for ((i=0; i<${#url_list[@]}; i++)); do
echo -n "${url_list[i]}..."
wget -o /dev/null -T3 --tries=1 --spider ${url_list[i]} &>/dev/null
[ $? -eq 0 ] && echo "可以访问" || echo "不可以访问"
done
echo -e "========================================\n"
echo -n "==========10秒后再次检查=========="
sleep 10
echo -e "\n"
}
function main() {
while true; do
check_url
done
}
main
执行效果:
text
[rich@rich-shell ~]$ bash check_web.sh
==========检查站点是否可以访问==========
http://www.baidu.com...可以访问
http://www.redhat.fun...不可以访问
http://www.redhat.com...可以访问
========================================
==========10秒后再次检查==========
==========检查站点是否可以访问==========
http://www.baidu.com...可以访问
http://www.redhat.fun...不可以访问
http://www.redhat.com...可以访问
========================================
==========10秒后再次检查==========^C
合格运维人员必会的脚本列表
作为一个合格的运维人员,需要掌握的脚本知识列表如下:
- 系统及各类服务的监控脚本,例如:文件、内存、磁盘、端口、URL 监控报警等。
- 监控网站目录下的文件是否被篡改,以及站点目录被批量篡改后如何批量恢复的脚本。
- 各类服务(Rsync、Nginx、MySQL 等)的启动及停止专业脚本。
- MySQL 主从复制监控报警,以及自动处理不复制故障的脚本。
- 一键配置 MySQL 多实例、一键配置 MySQL 主从部署的脚本。
- 监控 HTTP、MySQL、Rsync、NFS、Memcached 等服务是否异常的生产脚本。
- 一键软件安装及优化的脚本,比如 LANMP、Linux 一键优化、一键数据库安装与优化等。
- MySQL 多实例启动脚本,分库、分表自动备份脚本。
- 根据网络连接数及 Web 日志 PV 数封 IP 的脚本。
- 监控网站 PV 及流量,并对流量信息进行统计的脚本。
- 检查 Web 服务器多个 URL 地址是否异常的脚本(可批量处理且通用)。
- 对系统基础配置一键优化的脚本。
- TCP 连接状态及 IP 统计报警的脚本。
- 批量创建用户并设置随机 8 位密码的脚本。