2018-04-24 脚本进阶4 数组、字符串处理、expect

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中的所有大写字母转换为小写

  • 变量赋值


    image.png
[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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容