目录
1.Linux Shell简介
1.1.Shell概述
Shell本身是一个用C语言编写的程序,它是用户使用Unix/Linux的桥梁,用户的大部分工作都是通过Shell完成的。
Shell既是一种命令语言,又是一种程序设计语言。作为命令语言,它交互式地解释和执行用户输入的命令。作为程序设计语言,它定义了各种变量和参数,并提供了许多在高级语言中才具有的控制结构,包括循环和分支。
Shell它虽然不是Unix/Linux系统内核的一部分,但它调用了系统核心的大部分功能来执行程序、建立文件并以并行的方式协调各个程序的运行。Shell是用户与内核进行交互操作的一种接口,目前最流行的Shell称为bash Shell(Bourne Again Shell)。
Shell是一门编程语言(解释型),即shell脚本(就是用在Linux的shell命令编程),shell脚本程序从脚本中一行一行读取并执行这些命令,相当于一个用户把脚本中的命令一行一行敲到Shell提示符下执行。
Shell是一种脚本语言,那么,就必须有解释器来执行这些脚本。Unix/Linux上常见的Shell脚本解释器有bash、sh、csh、ksh等,习惯是把它们称作一种Shell。我们常说有多少种Shell,其实说的是Shell脚本解释器,可以通过cat /etc/shells 命令查看系统中安装的shell,不同的shell可能支持的命令语法是不同的。
sh是Unix标准默认的shell,由Steve Bourne开发,是Bourne Shell的缩写。
bash是Linux标准默认的shell,本教程也基于bash讲解。bash由Brian Fox和Chet Ramey共同完成,是Bourne Again Shell的缩写。
Shell本身支持的命令并不多,内部命令一共有40个,但是它可以调用其他的程序,每个程序就是一个命令,这使得Shell命令的数量可以无限扩展,其结果就是Shell的功能非常强大,完全能够胜任Linux的日常管理工作,如文本或字符串检索、文件的查找或创建、大规模软件的自动部署、更改系统设置、监控服务器性能、发送报警邮件、抓取网页内容、压缩文件等。
1.2.Shell基本格式
代码写在普通文件中,通常以.sh结尾,虽然不是强制要求,但希望大家最好这么做。
例子:
[root@hadoop02 bin]# vim helloworld.sh
编辑文件内容如下:
#!/bin/bash ##指定默认解释器,若执行时指定了解释器,则该句无效
echo "hello world" ##注释也可以写这里
保存退出即可
在这里,我们就写好了一个shell脚本,第一行是固定需要的,表明用哪一种shell解释器来执行我们的这个脚本程序。本质上,shell脚本里面的代码就是一些流程控制语句加一些特殊语法再加shell命令组成。其中,我们可以当做每一个命令就是shell编程当中的关键字。
1.3.Shell执行方式
1.3.1. sh方式或者bash方式
sh helloworld.sh
bash helloworld.sh ##直接指定系统默认的bash shell解释执行
1.3.2. source方式或者 . 命令方式
source命令也称为“点命令”,也就是一个点符号(.),是bash的内部命令。
功能:使Shell 先读入指定的Shell 程序文件,再依次执行文件中的所有语句。
source命令通常用于重新执行刚修改的初始化文件,比如/etc/profile文件,使之立即生效,而不必注销并重新登录。
用法:source filename 或 . filename
示例:
[root@hadoop02 bin]# . helloworld.sh
[root@hadoop02 bin]# source helloworld.sh
注意: . 和脚本名称之间是有空格的
1.3.3.直接执行该脚本文件
可以有2种方式,不过这2种方式都需要该文件有执行权限。
方式一:相对路径
[root@hadoop02 ~]# ./helloworld.sh
方式二:绝对路径
[root@hadoop02 ~]# /root/helloworld.sh
1.4.Shell注释
单行注释:Shell脚本中以#开头的代码就是注释
多行注释:Shell脚本中也可以使用多行注释,格式: :<<! 注释文字 !
2.Shell基本语法
2.1.变量
2.1.1.系统变量
Linux Shell中的变量分为“系统变量“和”用户自定义变量“。系统变量可以通过set命令来查看,用户环境变量可以通过env查看。
常用的系统变量:$PWD $SHELL $USER $HOME。
2.1.2.自定义变量
1. 语法
变量=值(例如:STR=abc)
##等号两侧不能有空格
##变量名一般习惯为大写
##使用变量:STR
echo $STR
echo '$STR'
echo "$STR"
2. 示例
解释
命令:ABC=huang bo,定义变量时中间带有空格,那么一定要带引号,否则不能定义。
命令:ABC='huang bo',带了单引号则原样输出,表示引号中间的值是整体字符串。
命令:ABC="huang bo",带了双引号,表示字符串中运行出现引用变量和转义字符等。
在引号当中要引用变量的时候,单引号和双引号就有区别了:
命令:echo 'xu zheng $ABC' 和 echo “xu zheng $ABC”
请看区别:
如果是单引号,则引号当中的任何东西都当做字符串,即特殊字符会被脱意;
如果是双引号,那么$ABC能打印出变量的值。
单引号和双引号总结:
单引号:
- 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的。
- 单引号字符串中不能出现单引号(对单引号使用转义字符也不行)
双引号:
- 双引号里可以有变量
- 双引号里可以出现转义字符
那假如命令是这样的:echo "xu zheng $ABCabc",请问还能不能打印出ABC变量的值呢?请看结果:
解决方法:把变量名用大括号括起来。
3. 变量高级用法
撤销变量:unset ABC
声明静态变量:readonly ABC='abc' #特点是这种变量是只读的,不能unset
请先看一个例子,我们现在写两个脚本,在a.sh中调用b.sh执行,那我们想知道a脚本能不能获取到b脚本的变量,b脚本能不能获取到a脚本的变量?
[root@hadoop02 bin]# vi a.sh
#!/bin/bash
A="A in a.sh"
echo $A
echo $B
/root/bin/b.sh
[root@hadoop02 bin]# vi b.sh
#!/bin/bash
B="B in b.sh"
echo $A
echo $B
执行 ./a.sh之后的结果:
在a.sh中只打印了变量A的值,这个好理解,因为shell是顺序解析执行的,在打印变量B的时候,就算它能获取b.sh中的变量,它也还没执行,所以,可定获取不到。
那再看b.sh打印出来的结果:可以发现虽然a.sh当中的语句执行完了再调用b.sh来执行,但是b.sh脚本依然也没法获取到a.sh的变量。
那怎么解决呢?使用export关键字,即export A="A in a.sh",这意味着把变量提升为当前shell进程中的全局环境变量,可供其他子shell程序使用,A变量就成了a.sh脚本所在bash进程的全局变量,该进程的所有子进程都能访问到变量A。
另一种使用方式:
如果在a.sh脚本中用如下方式调用b.sh:
. /root/bin/b.sh ##注意:重点关注前面那个点命令
或者 source /root/bin/b.sh
用上述两种方式意味着:b.sh就在a.sh所在bash进程空间中运行,而不是单独开一个子进程。
总结:
- a.sh中直接调用b.sh,会让b.sh在A所在的bash进程的子进程空间中执行。
- 而子进程空间只能访问父进程中用export定义的变量。
- 一个shell进程无法将自己定义的变量提升到父进程空间中去。
- source 或者 "." 号执行脚本时,会让脚本在调用者所在的shell进程空间中执行。
4. 反引号赋值
a=`ls -l /roo/bin` ##反引号,运行里面的命令,并把结果返回给变量a
另一种写法:a=$(ls -l /root/bin)
5. 变量有用技巧
${var} #变量本来的值
${var:-word} #如果变量var为空或已被删除(unset),那么返回word,但不改变var的值
${var:+word} #如果变量var被定义,那么返回word,但不改变var的值
${var:=word} #如果变量var为空或已被删除,那么返回word,并将var的值设置为word
${var:?message} #如果变量 var 为空或已被删除(unset),那么将消息 message 送到标准错误输出,可以用来检测变量 var 是否可以被正常赋值。若此替换出现在Shell脚本中,那么脚本将停止运行。
2.1.3.特殊变量
先来看看各个常用的特殊变量的概念:
$?:表示上一个命令退出的状态码
$$:表示当前进程编号
$0:表示当前脚本名称
$n:表示n位置的输入参数(n代表数字,n>=1)
$#:表示参数的个数,常用于循环
$* 和 $@:都表示参数列表
示例:
[root@hadoop02 bin]# vi d.sh
#!/bin/bash
echo "test var"
echo $$
echo $0
echo $1
echo $2
echo $#
echo $*
echo $@
echo $?
$*与$@区别:
- $* 和 $@ 都表示传递给函数或脚本的所有参数。
- 不被双引号" "包含时,$* 和 $@ 都以$1 $2 … $n 的形式组成参数列表。
- 当它们被双引号" "包含时,
- "$*" 会将所有的参数作为一个整体,以"$1 $2 … $n"的形式组成一个整串;
- "$@" 会将各个参数分开,以"$1" "$2" … "$n" 的形式组成一个参数列表
区别示例:
[root@hadoop02 bin]# vi e.sh
#!/bin/bash
echo $*
echo $@
echo "$*"
echo "$@"
echo --------------------
for a in $*
do echo $a
done
echo --------------------
for b in $@
do echo $b
done
echo --------------------
for a in "$*"
do echo $a
done
echo --------------------
for b in "$@"
do echo $b
done
2.2.运算符
2.2.1.算术运算符
1.用exper
2.用(( ))
3.用$[ ]
4.用let
5.注意:以上命令都只对整型值有效,不适用于浮点数
如果有浮点数参与运算,可以将echo与bc命令结合起来使用,格式如下:
且看bc的一些强大作用:
[root@hdp1 ~]# echo "1.121*3" | bc
3.363
[root@hdp1 ~]# echo "scale=2;3/8" | bc
.37
[root@hdp1 ~]# echo "obase=2;127" | bc
1111111
[root@hdp1 ~]# echo "obase=10;ibase=2;101111111" | bc
383
[root@hdp1 ~]# echo "10^10" | bc
10000000000
[root@hdp1 ~]# echo "sqrt(100)" | bc
10
除了用bc做进制转换以外,还可以这样做:
echo $((base#number))
例子:
[root@hdp1 ~]# echo $((8#377))
255
[root@hdp1 ~]# echo $((025))
21
[root@hdp1 ~]# echo $((0xA4))
164
使用bc还可以用来比较浮点数的大小:
[root@hdp1 ~]# echo "1.2<2" | bc
1
[root@hdp1 ~]# echo "1.2>2" | bc
0
[root@hdp1 ~]# echo "1.2==2" | bc
0
[root@hdp1 ~]# echo "1.2!=2" | bc
1
三元运算符:
[root@hdp1 ~]# [ $(echo "1.2>2" | bc) -eq 1 ] && echo yes || echo no
no
[root@hdp1 ~]# [ $(echo "1.2<2" | bc) -eq 1 ] && echo yes || echo no
yes
2.2.2.关系运算符
下面给出一张关系运算符列表:
运算符 | 等同运算符 | 说明 |
-eq | == | 检测两个数据是否相等,相等返回true |
-ne | != | 检测两个数据是否相等,不等返回true |
-ge | >= | 检测左边的数据是否大于等于右边的,若是,则返回true |
-gt | > | 检测左边的数据是否大于右边的,若是,则返回true |
-le | <= | 检测左边的数据是否小于等于右边的,若是,则返回true |
-lt | < | 检测左边的数据是否小于右边的,若是,则返回true |
2.2.3.布尔运算符
下面给出一张布尔运算符列表:
运算符 | 等同运算符 | 说明 |
! | ! | 非运算,表达式为true,则返回false,否则返回true |
-a | && | 与运算,两个表达式都为true,则返回true |
-o | || | 或运算,有一个表达式为true,则返回true |
2.2.4.字符串运算符
下面给出一张字符串运算符列表:
运算符 | 说明 |
= | 检测两边字符串是否相等,相等返回true |
!= | 检测两边字符串是否相等,不相等返回true |
-z | 检测字符串的长度是否为0,为0返回true |
-n | 检测字符串的长度是否为0,不为0返回true |
str | 检测字符串是否为空,不为空返回true |
2.2.5.文件运算符
下面给出一张文件运算符列表:
运算符 | 说明 |
-d | 检测文件是否为目录,若是,则返回true |
-f | 检测文件是否为普通文件(既不是目录,也不是设备文件),若是,则返回true |
-e | 检测文件(或目录)是否存在,若是,则返回true |
-s | 检测文件是否为空(文件大小大于0),若不为空,则返回true |
-r | 检测文件是否可读,若是,则返回true |
-w | 检测文件是否可以写,若是,则返回true |
-x | 检测文件是否可执行,若是,则返回true |
-b | 检测文件是否为块设备文件,若是,则返回true |
-c | 检测文件是否为字符设备文件,若是,则返回true |
2.3.流程控制
2.3.1.if
1. 语法格式
if[ condition ]
then
statements
elif[ condition ]
then
statements
else
statements
fi
2. 示例程序
#!/bin/bash
read -p "please your name:" NAME
if [ $NAME = root ]
then echo "hello ${NAME},welcome!"
elif [ $NAME = hdp01 ]
then echo "hello ${NAME},welcome!"
else
echo "I do not know yue!"
fi
注意事项:
- if 与 [ ] 以空格间隔,elif 与 [ ] 以空格间隔
- 条件前后和中括号以空格间隔
- 判断是否相等用 = ,而其前后以空格间隔(有时间总结下什么时候需要空格,什么时候不需要空格。
3. 规则解释
[ condition ] (注:condition前后要有空格)
#非空返回true,可使用$?验证(0为true,>1为false)
[ hadoop ]
#空返回false
[root@hdp1 ~]# a=2
[root@hdp1 ~]# b=2
[root@hdp1 ~]# [ a = b ]
[root@hdp1 ~]# echo $?
1
[root@hdp1 ~]# [ $a = $b ]
[root@hdp1 ~]# echo $?
0
[root@hdp1 ~]# [ ]
[root@hdp1 ~]# echo $?
1
[root@hdp1 ~]# [ $a ]
[root@hdp1 ~]# echo $?
0
[root@hdp1 ~]# if [ a = b ];then echo ok;else echo notok;fi
notok
[root@hdp1 ~]# if [ $a = $b ];then echo ok;else echo notok;fi
ok
[root@hdp1 ~]# if [ a=b ];then echo ok;else echo notok;fi
ok
[root@hdp1 ~]# if [a=b];then echo ok;else echo notok;fi
-bash: [a=b]: command not found
notok
短路运算符(理解为三元运算符)
[ condiiton ] && ehco OK || echo NotOK
#条件满足,执行&&后面的语句;条件不满足,执行||后面的语句
[root@hdp1 ~]# [ a = b ] && ehco OK || echo NotOK
NotOK
[root@hdp1 ~]# [ $a = $b ] && echo OK || echo NotOK
OK
条件判断组合
条件判断组合有2种使用方式:[] 和 [[]]
注意它们的区别:
[[ ]]中逻辑组合使用&& || 符号
[ ]中逻辑组合使用 -a -o
[root@hdp1 ~]# if [ a = b -a b = b ];then echo ok;else echo notok;fi
notok
[root@hdp1 ~]# if [ a = b -o b = b ];then echo ok;else echo notok;fi
ok
[root@hdp1 ~]# if [[ a = b && b = b ]];then echo ok;else echo notok;fi
notok
[root@hdp1 ~]# if [[ a = b || b = b ]];then echo ok;else echo notok;fi
ok
常用判断运算:
1. 字符串比较
= 判断相等
!= 判断不相等
-z 字符串长度为0,返回true
-n 字符串长度不为0,返回true
[root@hdp1 ~]# if [ 'aa' = 'bb' ];then echo ok;else echo notok;fi
notok
[root@hdp1 ~]# if [ 'aa' != 'bb' ];then echo ok;else echo notok;fi
ok
[root@hdp1 ~]# if [ -z 'aa' ];then echo ok;else echo notok;fi
notok
[root@hdp1 ~]# if [ -n 'aa' ];then echo ok;else echo notok;fi
ok
[root@hdp1 ~]# if [ -z '' ];then echo ok;else echo notok;fi
ok
[root@hdp1 ~]# if [ -z ' ' ];then echo ok;else echo notok;fi
notok
[root@hdp1 ~]# if [ -n ' ' ];then echo ok;else echo notok;fi
ok
[root@hdp1 ~]# if [ -n '' ];then echo ok;else echo notok;fi
notok
2. 整数比较
-lt 小于 less than
-le 小于等于
-eq 等于
-gt 大于 great than
-ge 大于等于
-ne 不等于
[root@hdp1 ~]# if [ 2 -lt 3 ];then echo ok;else echo notok;fi
ok
3. 文件判断
-d 是否为目录
-f 是否为文件
-e 是否存在
[root@hdp1 ~]# if [ -d /bin ];then echo ok;else echo notok;fi
ok
[root@hdp1 ~]# if [ -f /bin/ls ];then echo ok;else echo notok;fi
ok
[root@hdp1 ~]# if [ -e /bin/ls ];then echo ok;else echo notok;fi
ok
2.3.2.while
1. 语法格式
while expresion
do
command
……
done
2. 示例程序
#!/bin/bash
i=1
while ((i<=3))
do
echo $i
let i++
done
或
#!/bin/bash
i=1
while [ $i -le 3 ]
do
echo $i
i=$((i+1))
done
2.3.3.case
2.3.4.for
1. 语法格式
for 变量 in 列表
do
command
……
done
2. 三种方式
方式一:
#!/bin/bash
for N in 1 2 3
do
echo $N
done
或者 for N in 1 2 3;do echo $N;done;
方式二:
[root@hdp1 ~]# for N in {1,2,3};do echo $N;done;
1
2
3
方式三:
[root@hdp1 ~]# for ((i=1;i<=3;i++));do echo "welcome ${i} time";done;
welcome 1 time
welcome 2 time
welcome 3 time
或者
#!/bin/bash
for ((i=1;i<=3;i++))
do
echo "welcome ${i} time"
done
2.3.5.until
1. 语法格式
until expression
do
command
……
done
说明:expression一般为条件表达式,如果返回值为false,则继续执行循环体内的语句,否则跳出循环。换句话说,循环体会一直执行,直到条件表达式expression为true。
2. 示例程序
#!/bin/bash
a=0
until [ $a -gt 3 ]
do
echo $a
a=$((a+1))
done
2.4.数组
在Shell中,用括号来表示数组,数组元素用“空格”符号分隔开。定义数组的一般形式为:
array_name=(value1 … valuen)
读取数组元素的格式是:${array_name[index]}
示例:
[root@hdp1 ~]# mingxing=(huangbo xuzheng wangbaoqiang)
[root@hdp1 ~]# echo $mingxing
huangbo
[root@hdp1 ~]# echo ${mingxing[2]}
wangbaoqiang
[root@hdp1 ~]# mingxing[3]=liujialing
[root@hdp1 ~]# echo ${mingxing[3]}
liujialing
[root@hdp1 ~]# echo ${mingxing[*]}
huangbo xuzheng wangbaoqiang liujialing
[root@hdp1 ~]# echo ${!mingxing[*]}
0 1 2 3
[root@hdp1 ~]# echo ${#mingxing[*]}
4
获取数组下标:
[root@hdp1 ~]# echo ${!mingxing[*]}
0 1 2 3
[root@hdp1 ~]# echo ${!mingxing[@]}
0 1 2 3
输出数组所有元素:
[root@hdp1 ~]# echo ${mingxing[*]}
huangbo xuzheng wangbaoqiang liujialing
[root@hdp1 ~]# echo ${mingxing[@]}
huangbo xuzheng wangbaoqiang liujialing
获取数组的长度:
[root@hdp1 ~]# echo ${#mingxing[*]}
4
[root@hdp1 ~]# echo ${#mingxing[@]}
4
数组对接:
[root@hdp1 ~]# mingxing+=(liyifei liuyufeng)
[root@hdp1 ~]# echo ${mingxing[*]}
huangbo xuzheng wangbaoqiang liujialing liyifei liuyufeng
删除数组元素:但是会保留对应的位置,就是该值的下标依然保留,会空着,之后还可填充值进来。
[root@hdp1 ~]# unset mingxing[0]
[root@hdp1 ~]# echo ${mingxing[*]}
xuzheng wangbaoqiang liujialing liyifei liuyufeng
[root@hdp1 ~]# echo ${!mingxing[*]}
1 2 3 4 5
[root@hdp1 ~]# echo ${mingxing[0]}
[root@hdp1 ~]# echo ${mingxing[1]}
xuzheng
遍历数组:
#!/bin/bash
IP=(192.168.1.1 192.168.1.2 192.168.1.3)
#数组遍历第一种方式
for ((i=0;i<${#IP[*]};i++))
do
echo ${IP[$i]}
done
#数组遍历第二种方式
for index in ${!IP[*]}
do
echo ${IP[$index]}
done
数组的分片:
${arr[@]:number1:length}
这里number1表示从下标number1开始取值,length表示往后去几个元素,即取到的新数组的长度。
[root@hdp1 ~]# arr=(1 2 3 4 5 6 7 8 9)
[root@hdp1 ~]# echo ${arr[@]:0:3}
1 2 3
2.5.函数使用
函数的语法使用示例:
#!/bin/bash
hello(){
echo "`date +%Y-%m-%d`"
return 2
}
#调用函数及获取返回值
hello
echo $?
[root@hdp1 ~]# . a.sh
2018-10-31
2
函数调用方式直接写函数名就OK了!
注意:
- 必须在调用函数之前先声明函数,shell脚本是逐行执行的,不会像其他语言一样先预编译。
- 函数返回值,只能通过$?系统变量获得,可以显示加:return返回,如果不加,将以最后一条命令的运行结果作为返回值。return后跟数值n(0-255)
脚本调试:
使用-x选项跟踪脚本调试shell脚本,能打印所执行的每一行命令以及当前状态,比如:
sh -x a.sh 或在代码中加入:set -x
[root@hdp1 ~]# sh -x a.sh
+ hello
++ date +%Y-%m-%d
+ echo 2018-10-31
2018-10-31
+ return 2
+ echo 2
2
2.6.函数参数
#!/bin/bash
funcWithParam(){
echo "第一个参数为$1!"
echo "第二个参数为$2!"
echo "第十个参数为$20!"
echo "第十个参数为${10}!"
echo "第十一个参数为${11}!"
echo "参数总数有$#个!"
echo "作为一个字符串输出所有参数$*!"
}
funcWithParam 1 2 3 4 5 6 7 8 9 34 73
[root@hdp1 ~]# . a.sh
第一个参数为1!
第二个参数为2!
第十个参数为20!
第十个参数为34!
第十一个参数为73!
参数总数有11个!
作为一个字符串输出所有参数1 2 3 4 5 6 7 8 9 34 73!
2.7.跨脚本调用函数
编写一个base.sh脚本,里面放一个test函数,再编写另一个other.sh脚本,里面引入base.sh脚本,并且调用test函数。
[root@hdp1 ~]# vim base.sh
#!/bin/bash
test(){
echo "hello"
}
[root@hdp1 ~]# vim other.sh
#!/bin/bash
. /root/base.sh ##引入脚本
test ##调用引入脚本当中的test函数
[root@hdp1 ~]# . other.sh
hello
3.Shell综合案例
3.1.打印九九乘法表
示例代码:
#!/bin/bash
for((i=1;i<=9;++i))
do
for((j=1;j<=i;j++))
do
echo -ne "$i*$j=$((i*j))\t"
done
echo
done
解释说明:
-n 不加换行符
-e 解释转义符
echo 换行
3.2.自动部署集群的JDK
1.需求描述
公司内有一个N台节点的集群,需要统一安装一些软件(jdk)。需要开发一个脚本,实现对集群中的N台节点批量自动下载、安装jdk。
2.思路
我们现在有一个JDK安装包在一台服务器上。那我们要实现这个目标:
- 把包传到每台服务器,或者通过本地yum源的方式去服务器上取。
- 给每台节点发送一个安装脚本,并且让脚本自己执行。
- 需要写一个启动脚本,用来执行以上两步操作。
3.expect使用
蛋疼点:假如在没有配置SSH免密登录的前提下,我们要使用scp命令从一台机器拷贝文件夹都另外的机器,会有人机交互的过程,那我们怎么让机器自己实现人机交互?
灵丹妙药:expect
命令 | 描述 |
set | 可以设置超时,也可以设置变量 |
timeout | 超时等待时间,默认10s |
spawn | 执行一个命令 |
expect " " | 匹配输出的内容 |
exp_continue | 继续执行下面匹配 |
思路:模拟该人机交互过程,在需要交互的情况下,通过我们的检测给输入提前准备好值即可。
示例:观看配置SSH免密登录过程。
实现脚本:
[root@hadoop02 bin]# vi testExpect.sh
#!/bin/bash
## 定义一个函数
sshcopyid(){
expect -c "
spawn ssh-copy-id $1
expect {
\"(yes/no)?\" {send \"yes\r\";exp_continue}
\"password:\" {send \"$2\r\";exp_continue}
}
"
}
## 调用函数执行
sshcopyid $1 $2
注意:如果机器没有expect,则请先安装expect:yum install -y expect
4.脚本实现
1. 启动脚本initInstallJDK.sh
#!/bin/bash
SERVERS="192.168.123.201"
PASSWORD=hadoop
BASE_SERVER=192.168.123.202
auto_ssh_copy_id() {
expect -c "set timeout -1;
spawn ssh-copy-id $1;
expect {
*(yes/no)* {send -- yes\r;exp_continue;}
*password:* {send -- $2\r;exp_continue;}
eof {exit 0;}
}";
}
ssh_copy_id_to_all() {
for SERVER in $SERVERS
do
auto_ssh_copy_id $SERVER $PASSWORD
done
}
ssh_copy_id_to_all
for SERVER in $SERVERS
do
scp installJDK.sh root@$SERVER:/root
ssh root@$SERVER chmod 755 installJDK.sh
ssh root@$SERVER /root/installJDK.sh
done
2. 安装脚本installJDK.sh
#!/bin/bash
BASE_SERVER=192.168.123.202
yum install -y wget
wget $BASE_SERVER/soft/jdk-8u73-linux-x64.tar.gz
tar -zxvf jdk-8u73-linux-x64.tar.gz -C /usr/local
cat >> /etc/profile << EOF
export JAVA_HOME=/usr/local/jdk1.8.0_73
export PATH=\$PATH:\$JAVA_HOME/bin
EOF
4.总结
写脚本注意事项:
1.开头加解释器:#!/bin/bash 和注释说明。
2.命名建议规则:变量名大写、局部变量小写、函数名小写,名字体现出实际作用。
3.默认变量是全局的,在函数中变量local指定为局部变量,避免污染其他作用域。
4.set -e 遇到执行非0时退出脚本,set -x 打印执行过程。
5.写脚本一定要先测试再到生产上。