1、数组
- 定义
变量:存储单个元素的内存空间
数组:存储多个元素的连续的内存空间,相当于多个变量的集合
数组名和索引
索引:编号从0开始,属于数值索引
注意:索引可支持使用自定义的格式,而不仅是数值格式,即为关联索引,bash4.0版本之后开始支持
bash的数组支持稀疏格式(索引不连续)
声明数组:
declare -a ARRAY_NAME
declare -A ARRAY_NAME: 关联数组
注意:两者不可相互转换 - 数组的赋值
(1) 一次只赋值一个元素;
[root@centos6 boot]#title[0]=boss
[root@centos6 boot]#tile[1]=ceo
[root@centos6 boot]#tile[3]=cto
[root@centos6 boot]#tile[4]=ooo
(2) 一次赋值全部元素:
[root@centos6 boot]#name=(mage wang zhang li)
[root@centos6 app]#file=(/app/*.sh) ---支持列表式赋值
[root@centos6 app]#file=({a,b}.{log,txt}) ---也支持这个格式
(3) 只赋值特定元素:
[root@centos6 boot]#course=([0]=linux [2]=python [4]=golang)
(4) 交互式对数组值赋值
[root@centos6 boot]#read -a menu
hm lm yrt
[root@centos6 boot]#declare -a ---显示所有数组
declare -a BASH_ARGC='()'
declare -a BASH_ARGV='()'
declare -a BASH_LINENO='()'
declare -a BASH_SOURCE='()'
declare -ar BASH_VERSINFO='([0]="4" [1]="1" [2]="2" [3]="2" [4]="release" [5]="x86_64-redhat-linux-gnu")'
declare -a DIRSTACK='()'
declare -a FUNCNAME='()'
declare -a GROUPS='()'
declare -a PIPESTATUS='([0]="0")'
declare -a course='([0]="linux" [2]="pyton" [4]="golang")'
declare -a menu='([0]="hm" [1]="lm" [2]="yrt")'
declare -a name='([0]="mage" [1]="wang" [2]="zhang" [3]="li")'
declare -a tile='([1]="ceo" [3]="cto" [4]="ooo")'
declare -a title='([0]="boss")'
总结:数组可以使一个变量同时被赋值多个元素,由数组名和索引组
成,数组名就是变量的名字,索引编号从零开始。
- 数组引用
[root@centos6 boot]#echo $name ---不写索引下标时表示引用下标为0的元素
mage
[root@centos6 boot]#echo ${name[1]} ---引用某一个数组
wang
[root@centos6 boot]#echo ${name[*]} ---引用所有数组
mage wang zhang li
[root@centos6 boot]#echo ${name[@]} ---引用所有数组
mage wang zhang li
[root@centos6 boot]#echo ${#name[*]} ---显示数组元素的个数,比数组的索引下标多一个,因为索引下标是从0开始
4
[root@centos6 boot]#echo ${#name[@]} ---显示数组元素的个数
4
[root@centos6 boot]#unset title ---删除整个数组
[root@centos6 boot]#unset name[2] ---删除数组中的某个元素,使其变成稀疏模式
[root@centos6 boot]#declare -a
declare -a BASH_ARGC='()'
declare -a BASH_ARGV='()'
declare -a BASH_LINENO='()'
declare -a BASH_SOURCE='()'
declare -ar BASH_VERSINFO='([0]="4" [1]="1" [2]="2" [3]="2" [4]="release" [5]="x86_64-redhat-linux-gnu")'
declare -a DIRSTACK='()'
declare -a FUNCNAME='()'
declare -a GROUPS='()'
declare -a PIPESTATUS='([0]="0")'
declare -a course='([0]="linux" [2]="pyton" [4]="golang")'
declare -a menu='([0]="hm" [1]="lm" [2]="yrt")'
declare -a name='([0]="mage" [1]="wang" [3]="li")' ---name数组被变成稀疏模式
declare -a tile='([1]="ceo" [3]="cto" [4]="ooo")'
[root@centos6 boot]#echo ${menu[*]:2:1} ---表示跳过两个取一个
yrt
[root@centos6 app]#declare -A arr ---关联数组必须先声明,才能定义
[root@centos6 app]#arr=([0]=haha [a]=xixi [xxx]=hehe) ---关联数组索引编号可以是任意的字符,不一定非得是数字
[root@centos6 app]#declare -A ---查看关联数组
declare -A arr='([xxx]="hehe" [a]="xixi" [0]="haha" )'
示例
生成10个随机数保存于数组中,并找出其最大值和最小值
#!/bin/bash
#
declare -i min max
declare -a digit
for ((i=0;i<10;i++));do
digit[$i]=$RANDOM
[ $i -eq 0 ]&& max=${digit[$i]}&& min=${digit[$i]}&&continue
[ ${digit[$i]} -gt $max ]&&max=${digit[$i]}
[ ${digit[$i]} -lt $min ]&&min=${digit[$i]}
done
echo all digit are ${digit[*]}
echo max is $max
echo min is $min
编写脚本,定义一个数组,数组中的元素是/var/log目录下所有以.log结尾的文件;要统计其下标为偶数的文件中的行数之和
#!/bin/bash
#
declare -a file=(/var/log/*.log) ---声明一个数组,数组也是一个变量,只不过这个
变量可以有好几个值,数组加索引下标等于每一个值
echo "all file are ${file[*]}" ---打印一下数组中的所有元素
i=0
linesum=0
while [ $i -lt ${#file[*]} ];do ---数组的索引下标小于数组元素的个数的数字
line=`wc -l < ${file[$i]}` ---统计行数
[ $[i%2] -eq 0 ]&&let linesum+=line
echo "${file[$i]} line is $line"
let i++
done
echo all lines are $linesum
总结:数组其实也是一个变量,只不过这个变量中有很多个元素的集合,所以数组中的变量在引用时要加上$,普通的变量只有一个元素,所以用$file,而数组中有很多个元素,引用的时候要加上索引编号${file[$i]},并且加上大括号。
2、字符串管理
- 字符串切片
[root@centos6 app]#str=abcdefg
[root@centos6 app]#echo ${#str} ---显示字符串的长度
7
[root@centos6 app]#echo ${str:2} ----表示从左侧跳过两个字符取剩余的
cdefg
[root@centos6 app]#echo ${str:2:3} ---表示从左侧跳过两个字符取三个字符
cde
[root@redhat7 script]#echo ${str: -2} ---表示取倒数两个字符
fg
[root@redhat7 script]#echo ${str:3:-2} ---表示从左侧跳过三个,从右侧跳过两个取中间的
de
[root@redhat7 script]#echo ${str: -5:-2} ---表示从右向左先取5个字符,再从右边跳过两个字符,取中间的
cde
- 基于模式取子串
${var#*word}:其中word可以是指定的任意字符
功能:自左而右,查找var变量所存储的字符串中,第一次出现的word, 删除字符串开头至第一次出现word字符之间的所有字符
${var##*word}:同上,贪婪模式,不同的是,删除的是字符串开头至最后一次由word指定的字符之间的所有内容
示例:
file="/var/log/messages"
echo ${file#*/}
var/log/messages
echo ${file##*/}
messages
${var%word*}:其中word可以是指定的任意字符;
功能:自右而左,查找var变量所存储的字符串中,第一次出现的word, 删除字符串最后一个字符向左至第一次出现word字符之间的所有字符;
file="/var/log/messages"
echo ${file%/*}
/var/log
${var%%word*}:同上,只不过删除字符串最右侧的字符向左至最后一次出现word字符之间的所有字符;
示例:
url=http://www.magedu.com:80
echo ${url##*:}
80
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@centos6 app]#var=${str-haha};echo $var
haha
[root@centos6 app]#str=hehe
[root@centos6 app]#var=${str-haha};echo $var
hehe
表示str如果有值,$var就等于str的值,如果没有值,就等于haha。
3、declare命令和eval命令
declare [选项] 变量名
-r 声明或显示只读变量
-i 将变量定义为整型数
-a 将变量定义为数组
-A 将变量定义为关联数组
-f 显示此脚本前定义过的所有函数名及其内容
-F 仅显示此脚本前定义过的所有函数名
-x 声明或显示环境变量和函数
-l 声明变量为小写字母declare –l var=UPPER
-u 声明变量为大写字母declare –u var=lower
4、间接变量引用
如果第一个变量的值是第二个变量的名字,从第一个变量引用第二个变量的值就称为间接变量引用
variable1的值是variable2,而variable2又是变量名,variable2的值为value,间接变量引用是指通过variable1获得变量值value的行为
variable1=variable2
variable2=value
bash Shell提供了两种格式实现间接变量引用
eval tempvar=$$variable1
tempvar=${!variable1}
示例:
[root@server ~]# N=NAME
[root@server ~]# NAME=wangxiaochun
[root@server ~]# N1=${!N}
[root@server ~]# echo $N1
wangxiaochun
[root@server ~]# eval N2=\$$N
[root@server ~]# echo $N2
wangxiaochun
5、创建临时文件和安装复制文件
- mktemp命令:创建并显示临时文件,可避免冲突
mktemp[OPTION]... [TEMPLATE]
TEMPLATE: filename.XXX
X至少要出现三个
OPTION:
-d: 创建临时目录
-p DIR或--tmpdir=DIR:指明临时文件所存放目录位置
示例:
[root@centos6 app]#mktemp /app/tempfile.XXX ---创建临时文件,可
以发现在创建的同时在屏幕上显示,可能有些时候写脚本的时候能用到,注意X是大写
/app/tempfile.TkE
[root@centos6 app]#ls tempfile.TkE
tempfile.TkE
[root@centos6 app]#mktemp -d /app/tempdir.XXXX ---创建临时目录
/app/tempdir.d41H
[root@centos6 app]#mktemp -p /app tempfile.XXXXX --- -p选项用于
指定临时文件存放的目录位置,跟第一种创建临时文件类似
/app/tempfile.BHS2N
- 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
[root@centos6 app]#install -m 600 -o dufu -g dufu /etc/fstab /app/fstab
---表示复制文件并指定权限
[root@centos6 app]#ll fstab
-rw-------. 1 dufu dufu 899 Aug 31 22:50 fstab
[root@centos6 app]#install -m 700 -d /app/dir ---创建空目录并指定权限
[root@centos6 app]#ll -d dir/
drwx------. 2 root root 4096 Aug 31 22:51 dir/
[root@centos6 app]#mkdir -m 600 f66 ---跟这个类似
[root@centos6 app]#ll -d f66/
drw-------. 2 root root 4096 Aug 31 22:53 f66/
6、expect介绍
expect 是由Don Libes基于Tcl(Tool Command Language )语言开发的,主要应用于自动化交互式操作的场景,借助Expect处理交互的命令,可以将交互过程如:ssh登录,ftp登录等写在一个脚本上,使之自动化完成。尤其适用于需要对多台服务器执行相同操作的环境中,可以大大提高系统管理人员的工作效率。
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"}
匹配到hi后,会输出“you said hi”,并换行
多分支模式语法:
expect "hi" { send "You said hi\n" }
"hello" { send "Hello yourself\n" }
"bye" { send “Good bye\n" }
匹配hi,hello,bye任意字符串时,执行相应输出。等同如下:
expect {
"hi" { send "You said hi\n"}
"hello" { send "Hello yourself\n"}
"bye" { send “Good bye\n"}
}
示例1
cat ssh1.exp ---后缀不是.sh 而是.exp
#!/usr/bin/expect ---这个和脚本也不一样了
spawn ssh 192.168.8.100 ---启动这个进程,比如你要用ssh连到192.168.8.100这个主机
expect {
"yes/no" { send "yes\n";exp_continue } ---当捕捉到"yes/no" 关键字时就输出yes并换行
"password" { send "magedu\n" } ---当捕捉到"password"关键字时就输出magedu并换行,注意不是精确匹配,只要包含这个关键字就可以
}
interact ---表示允许进行交互,如果不加这一条,执行完这个expect就会从192.168.8.100这个主机中退出,
不会允许在这个主机上操作,只是运行了expect
#expect eof ---如果不加上面那行,而用这个,执行完就会退出,回到原来的终端
示例2 可以定义变量
cat ssh2.exp
#!/usr/bin/expect
set ip 192.168.8.100 ---也可以定义变量,相当于定义变量ip=192.168.8.100
set user root
set password magedu
set timeout 10
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$password\n" }
}
interact
示例3位置参数
vim ssh3.exp
#!/usr/bin/expect
set ip [lindex $argv 0] ---第一个参数赋值给变量ip
set user [lindex $argv 1] ---第二个参数赋值给变量user
set password [lindex $argv 2] ---第三个参数赋值给变量password
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$password\n" }
}
interact
./ssh3.exp 192.168.8.100 root magedu ---执行的时候后面的参数要和里面定义的变量对上
示例4执行多个命令
cat ssh4.exp
#!/usr/bin/expect
set ip [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
set timeout 10
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$password\n" }
}
expect "]#" { send "useradd haha\n" }
expect "]#" { send "echo magedu |passwd --stdin haha\n" }
send "exit\n"
expect eof
./ssh4.exp 192.168.8.100 root magedu
示例5:shell脚本调用expect
vim ssh5.sh
#!/bin/bash
ip=$1
user=$2
password=$3
expect <<EOF ---用多行重定向将expect读入脚本中
set timeout 10
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$password\n" }
}
expect "]#" { send "useradd lala\n" }
expect "]#" { send "echo magedu |passwd --stdin lala\n" }
expect "]#" { send "exit\n" }
expect eof
EOF
./ssh5.sh 192.168.8.100 root magedu