数组所谓数组,是有序的元素序列。若将有限个类型相同的变量的集合命名,那么这个名称为数组名。组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量。用于区分数组的各个元素的数字编号称为下标。数组是在程序设计中,为了处理方便, 把具有相同类型的若干元素按无序的形式组织起来的一种形式。 这些无序排列的同类数据元素的集合称为数组。
变量:存储单个元素的内存空间 ;
数组:存储多个元素的连续的内存空间,相当于多个变量的 集合
数组名和索引:索引:编号从0开始,属于数值索。注意:索引可支持使用自定义的格式,而不仅是数值格式 ,即为关联索引,bash4.0版本之后开始支持 。bash的数组支持稀疏格式(索引不连续)
声明数组: declare -a ARRAY_NAME
declare -A ARRAY_NAME: 关联数组
注意:两者不可相互转换
declare命令用于声明和显示已存在的shell变量。
declare -x varname 设置环境变量
declare -i 声明整数
declare -g 设置函数为全局函数
declare -xf 设置环境函数
declare -a 数组名 声明索引数组
declare -A 数组名 声明关联数组
declare -a 查看所有索引数组
declare -A 查看所有关系数组
数组元素的赋值
(1) 一次只赋值一个元素 ARRAY_NAME[INDEX]=VALUE
weekdays[0]="Sunday"
weekdays[4]="Thursday"
[root@centos7 ~]# title[0]=boss
[root@centos7 ~]# title[1]=ceo
[root@centos7 ~]# title[2]=coo
[root@centos7 ~]# declare -a |grep title
declare -a title='([0]="boss" [1]="ceo" [2]="coo")'
(2) 一次赋值全部元素 ARRAY_NAME=("VAL1" "VAL2" "VAL3" ...)
[root@centos7 ~]# name=(liubei guanyu zhangfei zhaoyun)
[root@centos7 ~]# declare -a |grep name
declare -a name='([0]="liubei" [1]="guanyu" [2]="zhangfei" [3]="zhaoyun")'
(3) 只赋值特定元素 ARRAY_NAME=([0]="VAL1" [3]="VAL2" ...)
[root@centos7 ~]# course=([0]=linux [2]=python [5]=oracle)
[root@centos7 ~]# declare -a |grep course
declare -a course='([0]="linux" [2]="python" [5]="oracle")'
(4) 交互式数组值对赋值 read -a ARRAY
[root@centos7 ~]# read -a menu
niuroumian caomian jidanmian
[root@centos7 ~]# declare -a |grep menu
declare -a menu='([0]="牛niuroumian" [1]="caomian" [2]="jidanmian")'
可以使用步进
[root@centos7 ~]# digit=({1..10})
[root@centos7 ~]# declare -a |grep digit
declare -a digit='([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="7" [7]="8" [8]="9" [9]="10"
可以使用通配符
root@(6-1)zuoye# ser=(./*.sh
root@(6-1)zuoye# declare -a |grep ser
declare -a ser='([0]="./10.sh" [1]="./11.sh" [2]="./1.sh" [3]="./2.sh" [4]="./4.sh" [5]="./5.sh" [6]="./7.sh" [7]="./8.sh" [8]="./caipiao.sh" [9]="./dengyao-sjx.sh.sh" [10]="./hans.sh" [11]="./juxing.sh" [12]="./qipan.sh" [13]="./shuzi-sjx.sh" [14]="./suiji1.sh" [15]="./until.sh")'
显示所有数组:declare -a (可以配合grep一起使用)
引用数组
引用数组元素:
${ARRAY_NAME[INDEX]}
注意:省略[INDEX]表示引用下标为0的元素
[root@centos7 ~]# echo $digit $数组名 #仅引用第一个值
1
[root@centos7 ~]# echo ${digit[1]} #指定下标以引用指定值
3
引用数组所有元素: 数组后加“*”和“@”
${ARRAY_NAME[*]}
${ARRAY_NAME[@]}
root@(6-1)zuoye# echo ${ser[*]}
./10.sh ./11.sh ./1.sh ./2.sh ./4.sh ./5.sh ./7.sh ./8.sh ./caipiao.sh ./dengyao-sjx.sh.sh ./hans.sh ./juxing.sh ./qipan.sh ./shuzi-sjx.sh ./suiji1.sh ./until.sh
root@(6-1)zuoye# echo ${ser[@]}
./10.sh ./11.sh ./1.sh ./2.sh ./4.sh ./5.sh ./7.sh ./8.sh ./caipiao.sh ./dengyao-sjx.sh.sh ./hans.sh ./juxing.sh ./qipan.sh ./shuzi-sjx.sh ./suiji1.sh ./until.sh
数组的长度(数组中元素的个数):数组前加“#”
${#ARRAY_NAME[*]}
${#ARRAY_NAME[@]}
root@(6-1)zuoye# echo ${#ser[@]}
16
root@(6-1)zuoye# echo ${#ser[*]}
16
引用最后一个值
${ARRAY_NAME[$[${#ARRAY_NAME[@]}-1]]}
root@(6-1)zuoye# echo ${ser[$[${#ser[@]}-1]]}
./until.sh
练习:分别显示每一个值,每个值换一行。
如
[root@centos7 ~]# echo ${test[@]}
hello hi good morning
test[0] is hello
test[1] is hi
test[2] is good
test[3] is morning
方式一:
#在终端输入,同bash中
[root@centos7 ~]# for n in $(seq 0 $[${#testshuzu[*]}-1]);do
> echo testshuzu[$n] is ${testshuzu[$n]}
> done
testshuzu[0] is liubei
testshuzu[1] is guanyu
testshuzu[2] is zhaofei
testshuzu[3] is huangzhong
testshuzu[4] is zhaoyun
方式二:
#在脚本中编辑, 需要从新定义数组,
#!/bin/bash
ser=({1..10}) #从新定义一个数组
declare -a | grep ser #输出数组中的变量值
or i in $(seq 0 $[${#ser[@]}-1] ) ;do #取出 变量最后一个值,进行循环。
echo "ser [$i] is ${ser[$i]}"
done
方式二的输出:
root@(6-1)zuoye# ./lin
declare -a ser='([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="7" [7]="8" [8]="9" [9]="10")'
ser [0] is 1
ser [1] is 2
ser [2] is 3
ser [3] is 4
ser [4] is 5
ser [5] is 6
ser [6] is 7
ser [7] is 8
ser [8] is 9
ser [9] is 10
在索引数组中加值,加到最后(最后一个值为n-1,加一个值就是n-1+1,就等于n)
在索引数组中加值,加到最后
[root@centos7 ~]# testshuzu[${#testshuzu[*]}]=machao
[root@centos7 ~]# for n in $(seq 0 $[${#testshuzu[*]}-1]);do echo testshuzu[$n] is ${testshuzu[$n]}; done
testshuzu[0] is liubei
testshuzu[1] is guanyu
testshuzu[2] is zhaofei
testshuzu[3] is huangzhong
testshuzu[4] is zhaoyun
testshuzu[5] is machao
删除数组中的某元素:导致稀疏格式
unset ARRAY[INDEX]
root@(6-1)zuoye# unset ser[0]
root@(6-1)zuoye# declare -a |grep ser
declare -a ser='([1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="7" [7]="8" [8]="9" [9]="10") #显示结果只少了ser[0]
删除整个数组:
unset ARRAY
root@(6-1)zuoye# unset ser
root@(6-1)zuoye# declare -a |grep ser
root@(6-1)zuoye# #数组整个删除没事显示
数组数据处理
引用数组中的元素:
数组切片:${ARRAY[@]:offset:number}
offset: 要跳过的元素个数
number: 要取出的元素个数
取偏移量之后的所有元素
${ARRAY[@]:offset}
[root@centos7 ~]# echo ${testshuzu[*]:1:3}
zhaofei weiyan zhaoyun
[root@centos7 ~]# echo ${testshuzu[*]:1} 跳过一个值,取到最后
zhaofei weiyan zhaoyun machao machao
向数组中追加元素:
ARRAY[${#ARRAY[*]}]=value
关联数组:
两种方式:
一:先声明,再赋值
declare -A ARRAY_NAME
declare -A testshuzu
testshuzu=([zhugong]=liubei [erdi]=guanyu [sandi]=zhangfei)
二:声明,赋值一步到位
ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2‘...)
declare -A weiguo=([chengxiang]=caocao [junshi]=simayi [jiangju]=xiahoudun )
注意:关联数组必须先声明再调用
写一个脚本会生成一个名为digit的数组包含10个随机数,显示数组的所有值,再显示最大值,最小值。
$RANDOM 0-32767
./digit.sh
All digit are .....
Max is
Min is
方法1:
fun1() {
for (( i=0;i<10;i++ ));do
a=$(echo $RANDOM)
digit[$i]=$a
echo ${digit[$i]}
done
}
fun1 |sort -nr > /app/shuzul
cat /app/shuzul
m=$( cat /app/shuzul |head -1)
n=$( cat /app/shuzul |tail -1)
echo max=$m
echo min=$n
rm /app/shuzul -rf
方法2:
#!/bin/bash
declare -i min max
declare -a digit
for ((i=0;i<10;i++));do
digit[$i]=$RANDOM
[ $i -eq 0 ] && min=${digit[0]} && max=${digit[0]} && continue
[ ${digit[$i]} -gt $max ] && max=${digit[$i]} && continue
[ ${digit[$i]} -lt $min ] && min=${digit[$i]} && continue
done
echo All digit are ${digit[*]}
echo Max is $max
echo Min is $min
编写脚本,定义一个数组,数组中的元素是/var/log目录下所有以.log结尾的文件;统计出其下标为偶数的文件中的行数之和
方法1:
#!/bin/bash
Sum=0
File=(/var/log/*.log)
for ((i=0;i<=$[${#File[*]}-1];i++));do
if [ $[$i%2] -eq 0 ];then
Line=$(cat ${File[$i]} | wc -l)
Sum=$[$Sum+$Line]
fi
done
echo $Sum
方法2:
declare -a logfiles=(/var/log/*.log)
echo All logfile name ${logfiles[*]}
i=0
linesum=0
while [ $i -lt ${#logfiles[*]} ];do
[ $[i%2] -eq 0 ]&& fileline=$(cat ${logfiles[$i]} |wc -l ) && let linesum+=$fileline
let i++
done
echo All lines are $linesum
字符串字符串切片
${#var}:返回字符串变量var的长度
echo ${#name} echo ${#name[1]}
显示变量或数组的字符数
root@(6-1)zuoye# name=wang123
root@(6-1)zuoye# echo ${#name}
7
root@(6-1)zuoye# echo ${#name[1]}
0
root@(6-1)zuoye# echo $name
wang123
${var:offset}:返回字符串变量var中从第offset个字符后(不包括 第offset个字符)的字符开始,到最后的部分,offset的取值在0 到 ${#var}-1 之间(bash4.2后,允许为负值)
root@(6-1)zuoye# echo ${name:2} #从左边跳过2个字符,取到最后
ng123
${var:offset:number}:返回字符串变量var中从第offset个字符后 (不包括第offset个字符)的字符开始,长度为number的部分
root@(6-1)zuoye# echo ${name:2:3} #从左边跳过2个字符,取3个字符
ng1
${var: -length}:取字符串的最右侧几个字符
root@(6-1)zuoye# echo ${name: -2} #从右向左取2个字符,注意冒号后至少打一个空格
23
注意:冒号后必须有一空白字符
${var:offset:-length}:从最左侧跳过offset字符,一直向右取到 距离最右侧lengh个字符之前的内容
[root@centos7 ~]# echo ${name:3:-1} #从左边跳过3个字符,从右边跳过1个字符
r1
root@(6-1)zuoye# echo ${name: -3:1} #从右向左取3个字符,再从左向右取1个字符
1
${var: -length:-offset}:先从最右侧向左取到length个字符开始 ,再向右取到距离最右侧offset个字符之间的内容
name=qwer12
root@(6-1)zuoye# echo ${name:-3:-1} #从右向左取3个字符,再从右向左去掉1个字符
r1
注意:-length前空格
字符串处理
基于模式取子串
${var#*word}:其中word可以是指定的任意字符 功能:自左而右,查找var变量所存储的字符串中,第一 次出现的word, 删除字符串开头至第一次出现word字符之间的 所有字符
从左至右查找
[root@centos7 ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@centos7 ~]# echo ${PATH#*local}
/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
${var##*word}:同上,贪婪模式,不同的是,删除的 是字符串开头至最后一次由word指定的字符之间的所有内容
示例:
file="var/log/messages“
${file#*/}: log/messages
${file##*/}: messages
[root@centos7 ~]# echo ${PATH##*local} 贪婪模式
/bin:/usr/sbin:/usr/bin:/root/bin
${var%word*}:其中word可以是指定的任意字符;
功能:自右而左,查找var变量所存储的字符串中,第一 次出现的word, 删除字符串最后一个字符向左至第一次出现 word字符之间的所有字符;
file="/var/log/messages"
${file%/*}: /var/log
从左至右查找
[root@centos7 ~]# echo ${PATH}
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@centos7 ~]# echo ${PATH%local*}
/usr/local/sbin:/usr/
${var%%word*}:同上,只不过删除字符串最右侧的字符向 左至最后一次出现word字符之间的所有字符;
[root@centos7 ~]# echo ${PATH%%local*}
/usr/
[root@centos7 ~]# url=http://www.magedu.com:8080
[root@centos7 ~]# echo ${url%%:*}
http
[root@centos7 ~]# echo ${url##*:}
8080
[root@centos7 ~]# url=http://www.magedu.com:8080
[root@centos7 ~]# echo ${url%%:*}
http
[root@centos7 ~]# echo ${url##*:}
8080
示例:
${url##*:} 80
${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@centos7 ~]# name="Zhangfei LiuBeI"
[root@centos7 ~]# echo ${name,,}
zhangfei liubei
[root@centos7 ~]# echo ${name^^}
ZHANGFEI LIUBEI
高级变量用法-有类型变量
Shell变量一般是无类型的,但是bash Shell提供了declare和 typeset两个命令用于指定变量的类型,两个命令是等价的
declare [选项] 变量名
-r 声明或显示只读变量
-i 将变量定义为整型数
-a 将变量定义为数组
-A 将变量定义为关联数组
-f 显示已定义的所有函数名及其内容
-F 仅显示已定义的所有函数名
-x 声明或显示环境变量和函数
-l 声明变量为小写字母 declare –l var=UPPER
-u 声明变量为大写字母 declare –u var=lower
eval命令
eval命令将会首先扫描命令行进行所有的置换,然后再执行该 命令。该命令适用于那些一次扫描无法实现其功能的变量.该 命令对变量进行两次扫描
[root@centos7 ~]# mage=linux
[root@centos7 ~]# linux=centos
[root@centos7 ~]# eval echo \$$mage
centos
[root@centos7 ~]# centos=redhat
[root@centos7 ~]# eval echo \$$(eval echo \$$mage)
redhat
[root@centos7 ~]# n=10
[root@centos7 ~]# echo {1..$n}
{1..10}
[root@centos7 ~]# eval echo {1..$n}
1 2 3 4 5 6 7 8 9 10
间接变量引用
如果第一个变量的值是第二个变量的名字,从第一个变量引 用第二个变量的值就称为间接变量引用
variable1的值是variable2,而variable2又是变量名, variable2的值为value,间接变量引用是指通过variable1获 得变量值value的行为
variable1=variable2
variable2=value
bash Shell提供了两种格式实现间接变量引用
eval tempvar=\$$variable1
tempvar=${!variable1}
[root@centos7 ~]# echo $mage
linux
[root@centos7 ~]# echo $linux
centos
[root@centos7 ~]# test=${!mage}
[root@centos7 ~]# echo $test
centos
创建临时文件
mktemp命令:创建并显示临时文件,可避免冲突
mktemp [OPTION]... [TEMPLATE]
TEMPLATE: filename.XXX
X至少要出现三个
OPTION:
-d: 创建临时目录
-p DIR或--tmpdir=DIR:指明临时文件所存放目录位置
示例:
mktemp /tmp/test.XXX
tmpdir=`mktemp –d /tmp/testdir.XXX`
mktemp --tmpdir=/testdir test.XXXXXX
安装复制文件
install命令:
install [OPTION]... [-T] SOURCE DEST 单文件
install [OPTION]... SOURCE... DIRECTORY
install [OPTION]... -t DIRECTORY SOURCE...
install [OPTION]... -d DIRECTORY...创建空目录
选项:
-m MODE,默认755
-o OWNER
-g GROUP
示例:
install -m 700 -o wang -g admins srcfile desfile
install –m –d /testdir/installdir
install -m 700 -o liubei -g root /etc/passwd /app/hellopasswd
expect介绍
expect 是由Don Libes基于Tcl( Tool Command Language )语言开发的,主要应用于自动化交互式操作的场景,借助 Expect处理交互的命令,可以将交互过程如:ssh登录,ftp 登录等写在一个脚本上,使之自动化完成。尤其适用于需要 对多台服务器执行相同操作的环境中,可以大大提高系统管 理人员的工作效率
expect命令
expect 语法:
expect [选项] [ -c cmds ] [ [ -[f|b] ] cmdfile ] [ args ]
选项
-c:从命令行执行expect脚本,默认expect是交互地执行的
示例:expect -c 'expect "\n" {send "pressed enter\n"}
-d:可以输出输出调试信息
示例:expect -d ssh.exp
expect中相关命令
spawn:启动新的进程
send:用于向进程发送字符串 ,就是执行交互动作
expect:从进程接收字符串
interact:允许用户交互
exp_continue 匹配多个字符串在执行动作后加此命令
expect最常用的语法(tcl语言:模式-动作)
单一分支模式语法:
expect "hi" {send "You said hi\n"}
root@(6-1)~# expect #进入expect交互式模式
expect1.3> expect "hi" {send "You said hi\n"} # expect 从进程接收字符串 , send:用于向进程发送字符串 ,
hi
You said hi
匹配到hi后,会输出“you said hi”,并换行
多分支模式语法:
expect "hi" { send "You said hi\n" } \
"hehe" { send “Hehe yourself\n" } \
"bye" { send “Good bye\n" }
下图中红色标识为接受发送词汇,橙色标识为匹配变量,和交互式输出变量
匹配hi,hello,bye任意字符串时,执行相应输出。等同如下:
expect {
"hi" { send "You said hi\n"}
"hehe" { send "Hehe yourself\n"}
"bye" { send “Good bye\n"
}
脚本实现免密码登录
方法一:
#!/usr/bin/expect
#time:2018.3.24
#edition:1.1
#effect:Interactive logon automatic input password(交互式登录自动输入密码)
#expect格式开头
#spawn新建进程,也就是ssh的连接
spawn ssh root@172.18.250.249
#expect 从进程接收字符串 ,设置接收字符串
#send 向进程发送字符串,设置密码...
#exp_continue,需要匹配的次数不止一次,所以需要在前边,
#执行动作后加上exp_continue,以表示还有,后边还有动作,没有结束.
expect {
"yes/no" { send "yes\n" ;exp_continue }
"password" { send "centos\n" }
}
#interact允许用户交互,无论值执不执行脚本任务,最后停留在目标终端上。
interact
#expect eof表示结束,登陆上去执行完脚本任务,直接退出,不在目标终端上停留
#expect eof
~
方式二:
设置变量方式实现
#!/usr/bin/expect
#time:2018.3.24
#edition:1.1
#effect:Interactive logon automatic input password(交互式登录自动输入密码)
#设置三个变量
set user root
set remotehost 172.18.250.249
set password centos
spawn ssh $user@$remotehost
expect {
"yes/no" { send "yes\n" ;exp_continue }
"password" { send "$password\n" }
}
interact
#expect eof
方法三:
执行登录后的后续操作
设置三个位置变量来实现变量的赋值
#!/usr/bin/expect
#time:2018.3.24
#edition:1.1
#effect:Interactive logon automatic input password(交互式登录自动输入密码)
#设置三个位置变量
set user [lindex $argv 0]
set remotehost [lindex $argv 1]
set password [lindex $argv 2]
spawn ssh $user@$remotehost
expect {
"yes/no" { send "yes\n" ;exp_continue }
"password" { send "$password\n" }
}
expect "]#" { send "useradd liubei\n" }
expect "]#" { send "echo redhat|passwd --stdin liubei\n" }
send "exit\n"
expect eof
方法四:
使用shell脚本格式
通过EOF实现赋值。
#!/bin/bash
#time:2018.3.24
#edition:1.1
#effect:Interactive logon automatic input password(交互式登录自动输入密码)
user=$1
remotehost=$2
password=$3
expect << EOF
spawn ssh $user@$remotehost
expect {
"yes/no" { send "yes\n" ;exp_continue }
"password" { send "$password\n" }
}
expect "]#" { send "useradd liubei\n" }
expect "]#" { send "echo redhat|passwd --stdin liubei\n" }
send "exit\n"
expect eof
EOF
精简的shell
#!/bin/bash
#time:2018.3.24
#edition:1.1
#effect:Interactive logon automatic input password(交互式登录自动输入密码)
user=$1
remotehost=$2
password=$3
expect << EOF
spawn ssh $user@$remotehost
expect {
"yes/no" { send "yes\n" ;exp_continue }
"password" { send "$password\n" }
}
expect "]#" { send "useradd liubei\n" }
expect "]#" { send "id liubei" }
interact
EOF