一、变量的定义
yum provides
:查询某个程序所在安装包,查看文件(命令)属于哪个软件
如已知apache 的一个压测工具名为:ab,但是不知道应该安装那个安装包的情况,使用yum provides指令参考如下:
[syt@Nchu150412 ~]$ yum provides rz
已加载插件:fastestmirror, langpacks
Determining fastest mirrors
* base: mirrors.bupt.edu.cn
* extras: mirrors.bupt.edu.cn
* updates: mirrors.aliyun.com
base/7/x86_64/filelists_db | 7.2 MB 00:00:02
extras/7/x86_64/filelists_db | 277 kB 00:00:00
lrzsz-0.12.20-36.el7.x86_64 : The lrz and lsz modem communications programs
源 :base
匹配来源:
文件名 :/usr/bin/rz
lrzsz-0.12.20-36.el7.x86_64 : The lrz and lsz modem communications programs
源 :@anaconda
匹配来源:
文件名 :/bin/rz
lrzsz-0.12.20-36.el7.x86_64 : The lrz and lsz modem communications programs
源 :@anaconda
匹配来源:
文件名 :/usr/bin/rz
./configure
是源代码安装的第一步,主要的作用是对即将安装的软件进行配置,检查当前的环境是否满足要安装软件的依赖关系。
yum clean all
清理yum缓存
yum makecache
缓存软件包信息
alias
#查看系统当前所有的别名
alias h5='head -5
’ #定义新的别名,这时候输入h5就等于输入’head -5’
1、常用的通配符
*
:匹配0或多个任意字符
?
:匹配任意单个字符
[list]
:匹配[list]中的任意单个字符,或者一组单个字符,[123],[1-13]为1,3。
[!list]
:匹配除list中的任意单个字符
{string1,string2,...}
:匹配string1,string2或更多字符串,{1…13}是1到13的意思
2、bash中的引号
双引号"":会把引号的内容当成整体来看待,允许通过$符号引用其他变量值
单引号'':会把引号的内容当成整体来看代,禁止引用其他变量值,shell中特殊符号都被视为普通字符。
反撇号``:反撇号和$()一样,引号或括号里的命令会优先执行,如果存在嵌套,反撇号不能用。
[root@Nchu150412 a]# echo "$(date +%F)"
2022-09-23
[root@Nchu150412 a]# echo "`date +%F`"
2022-09-23
[root@Nchu150412 a]# echo `echo "`date +%F`"`
-bash: command substitution:行1: 寻找匹配的 `"' 是遇到了未预期的文件结束符
-bash: command substitution:行2: 语法错误: 未预期的文件结尾
-bash: command substitution:行1: 寻找匹配的 `"' 是遇到了未预期的文件结束符
-bash: command substitution:行2: 语法错误: 未预期的文件结尾
date +%F
[root@Nchu150412 a]# echo $(echo "`date +%F`")
2022-09-23
shell就是人机交互的一个桥梁
3、shell脚本的基本写法
1、脚本第一行,魔法字符#!
指定解释器【必写】
#!/bin/bash
表示以下内容使用bash解释器解析
注意:
如果直接将解释器路径写死在脚本里,可能在某些系统就会存在找不到解释器的兼容性问题,所以可以使用:#!/bin/env 解释器
2、脚本第二部分,注释(#号)说明,对脚本的基本信息进行描述【可选】
#!/bin/env bash
#以下内容是对脚本的基本信息的描述
#Name:名字
#Desc:描述describe
#Path:存放路径
#Usage:用法
#Update:更新时间
#下面就是脚本的具体内容
commands
...
3、脚本第三部分,脚本要实现的具体代码内容。
注意:标准执行方式脚本必须要有可执行权限啊
标准执行方式:
/shell01/first_shell.sh
./first_shell.sh
非标准执行方式(不建议):
1、直接在命令行指定解释器执行
-x:一般用于排错,查看脚本的执行过程
-n:用来查看脚本的语法是否有问题 bash -n first_shell.sh
2、使用source命令读取脚本文件,执行文件里的代码
source first_shell.sh
[root@Nchu150412 a]# A=hello
[root@Nchu150412 a]# echo $A
hello
[root@Nchu150412 a]# echo ${A}
hello
[root@Nchu150412 a]# A=123456
[root@Nchu150412 a]# echo $A
123456
[root@Nchu150412 a]# unset A
[root@Nchu150412 a]# echo $A
[root@Nchu150412 a]#
4、变量的定义规则
1、变量名区分大小写
2、变量名不能有特殊符号
特别说明:对于有空格的字符串给变量赋值时,要用引号引起来
[root@Nchu150412 a]# A=hello world
bash: world: 未找到命令...
[root@Nchu150412 a]# A="hello world"
[root@Nchu150412 a]# echo $A
hello world
3、变量名不能以数字开头
注意:不能以数字开头
4、等号两边不能有任何空格
[root@Nchu150412 a]# A =123
bash: A: 未找到命令...
[root@Nchu150412 a]# A= 123
bash: 123: 未找到命令...
[root@Nchu150412 a]# A = 123
bash: A: 未找到命令...
[root@Nchu150412 a]# A=123
[root@Nchu150412 a]# echo $A
123
5、变量名尽量做到见名知意
说明:一般变量名使用大写(小写也可以),不要同一个脚本中变量全是a,b,c等不容易阅读
5、变量定义方式有哪些?
(1)基本方式:直接赋值给一个变量
[root@Nchu150412 a]# A=1234567
[root@Nchu150412 a]# echo $A
1234567
[root@Nchu150412 a]# echo ${A:2:4} #表示从A变量中第3个字符开始截取,截取4个字符
3456
说明:
$变量名和${变量名}的异同
相同点:都可以调用变量
不同点:${变量名}可以只截取变量的一部分,而$变量名不可以
(2)命令执行结果赋值给变量—$()
3.10.0-1160.el7.x86_64
[root@Nchu150412 a]# B=$(uname -r)
[root@Nchu150412 a]# echo $B
3.10.0-1160.el7.x86_64
[root@Nchu150412 a]# A=$(hostname)
[root@Nchu150412 a]# echo $A
Nchu150412
(3)交互式定义变量(read)
目的:让用户自己给变量赋值,比较灵活
语法:read [选项] 变量名
常见选项:
选项 | 释义 |
---|---|
-p | 定义提示用户的信息 |
-n | 定义字符数(限制变量值的长度) |
-t | 定义超时时间,默认单位为秒(限制用户输入变量值的超时时间) |
-s | 不显示(不显示用户输入的内容) |
举例说明
用法1:用户自己定义变量值
[root@Nchu150412 a]# read name
harry
[root@Nchu150412 a]# echo $name
harry
[root@Nchu150412 a]# read -p "Input your name:" name
Input your name:jack
[root@Nchu150412 a]# echo $name
jack
[root@Nchu150412 a]# read -p "Input your password:" pass
Input your password:123
[root@Nchu150412 a]# echo $pass
123
[root@Nchu150412 a]# read -s -p "Input your password:" pass
Input your password:[root@Nchu150412 a]#
[root@Nchu150412 a]# echo $pass
123456
[root@Nchu150412 a]# read -p -n 5 "Input your name:" name #一定要把-n放在-p前面
-n
[root@Nchu150412 a]# read -n 5 -p "Input your name:" name #定义最多只能输入5个字符
Input your name:hhhhh[root@Nchu150412 a]# hh
[root@Nchu150412 a]# read -t 3 -p "name:" name #3秒之内没输入就自动取消输入
用法2:变量值来自文件
[root@Nchu150412 a]# vim ip.txt
[root@Nchu150412 a]# cat ip.txt
10.1.1.1
[root@Nchu150412 a]# read -p "qing shuru IP dizhi:" IP < ip.txt
[root@Nchu150412 a]# echo $IP
10.1.1.1
(4)定义有类型的变量(declare)
目的:给变量做一些限制,固定变量类型,比如:整型、只读
用法:declare 选项 变量名=变量值
常用选项:
[root@Nchu150412 a]# A=123
[root@Nchu150412 a]# echo $A
123
[root@Nchu150412 a]# A=hello
[root@Nchu150412 a]# echo $A
hello
[root@Nchu150412 a]# declare -i A=123
[root@Nchu150412 a]# echo $A
123
[root@Nchu150412 a]# A=hello
[root@Nchu150412 a]# echo $A
0
[root@Nchu150412 a]# A=333
[root@Nchu150412 a]# echo $A
333
[root@Nchu150412 a]# declare -r B=hello
[root@Nchu150412 a]# echo $B
hello
[root@Nchu150412 a]# B=8888
-bash: B: 只读变量
[root@Nchu150412 a]# unset B
-bash: unset: B: 无法反设定: 只读 variable
[root@Nchu150412 a]# AAAA=heima
[root@Nchu150412 a]# env|grep AAAA
[root@Nchu150412 a]# export AAAA
[root@Nchu150412 a]# env|grep AAAA
AAAA=heima
[root@Nchu150412 a]# declare -x BBBB=itcast
[root@Nchu150412 a]# env|grep BBBB
BBBB=itcast
6、变量的分类
(1)本地变量
本地变量:当前用户自定义的变量。当前进程中有效,其他进程及当前进程的子进程无效。
(2)环境变量
环境变量:当前进程有效,并且能够被子进程调用
1、env
查看当前用户的环境变量
[root@Nchu150412 ~]# ps
PID TTY TIME CMD
2571 pts/0 00:00:00 su
2585 pts/0 00:00:00 bash
2621 pts/0 00:00:00 ps
[root@Nchu150412 ~]# CCC=888
[root@Nchu150412 ~]# export CCC
[root@Nchu150412 ~]# env|grep CCC
CCC=888
[root@Nchu150412 ~]# su - syt
上一次登录:二 9月 27 09:21:36 CST 2022:0 上
[syt@Nchu150412 ~]$ ps
PID TTY TIME CMD
2497 pts/0 00:00:00 bash
2693 pts/0 00:00:00 bash
2738 pts/0 00:00:00 ps
[syt@Nchu150412 ~]$ env|grep CCC
#env的意思是打印当前的环境变量,竖线“|”的意思是管道,即将env命令所打印出来的东西传给竖线之后的命令处理。grep是正则表达式过滤命令。当遇到包含“CCC“的行时会显示该行。整个命令就是显示包含“CCC”关键字的环境变量。
[syt@Nchu150412 ~]$
2、set
查询当前用户的所有变量(临时变量与环境变量)
3、export
变量名=变量值 或者 变量名=变量值;export 变量名
(3)全局变量
全局变量:全局所有的用户和程序都能调用,且继承,新建的用户也默认能调用。
解读相关配置文件
说明:以上文件修改后,都需要重新source让其生效或者退出重新登陆。
用户登陆系统读取相关文件的顺序
1、/etc/profile
2、$HOME/.bash_profile
3、$HOME/.bashrc
4、/etc/.bashrc
5、$HOME/.bash_logout
(4)系统变量
系统变量(内置bash中变量):shell本身已经固定好了它的名字和作用。
[syt@Nchu150412 ~]$ ls
公共 模板 视频 图片 文档 下载 音乐 桌面
[syt@Nchu150412 ~]$ echo $?
0
[syt@Nchu150412 ~]$ lll
bash: lll: 未找到命令...
[syt@Nchu150412 ~]$ echo $?
127
[syt@Nchu150412 ~]$ ls /etc/aaaa
ls: 无法访问/etc/aaaa: 没有那个文件或目录
[syt@Nchu150412 ~]$ echo $?
2
举个例子:
[root@Nchu150412 a]# vim variable.sh
[root@Nchu150412 a]# ll
总用量 12
-rwxr-xr-x. 1 root root 207 9月 23 16:50 first.sh
-rw-r--r--. 1 root root 9 9月 26 10:55 ip.txt
-rw-r--r--. 1 root root 179 9月 27 14:22 variable.sh
[root@Nchu150412 a]# chmod +x variable.sh
[root@Nchu150412 a]# ll
总用量 12
-rwxr-xr-x. 1 root root 207 9月 23 16:50 first.sh
-rw-r--r--. 1 root root 9 9月 26 10:55 ip.txt
-rwxr-xr-x. 1 root root 179 9月 27 14:22 variable.sh
[root@Nchu150412 a]# ./variable.sh
$0 = ./variable.sh
$# = 0
$* =
$@ =
$1 =
$2 =
$3 =
$10 =
do you understand!
[root@Nchu150412 a]# ./variable.sh a b c
$0 = ./variable.sh
$# = 3
$* = a b c
$@ = a b c
$1 = a
$2 = b
$3 = c
$10 =
do you understand!
[root@Nchu150412 a]# ./variable.sh 1 2 3 4 5 6 7 8 9 10
$0 = ./variable.sh
$# = 10
$* = 1 2 3 4 5 6 7 8 9 10
$@ = 1 2 3 4 5 6 7 8 9 10
$1 = 1
$2 = 2
$3 = 3
$10 = 10
do you understand!
二、简单四则运算
算术运算:默认情况下,shell就只能支持简单的整数运算
运算内容:加(+)、减(-)、乘(*)、除(/)、求余数(%)
四则运算符号
注:用expr时,注意一定要空格。
let n=n**3:n的三次方
三、条件判断语法结构
1、条件判断语法格式
格式1:test 条件表达式
格式2:[ 条件表达式 ]
格式3:[[ 条件表达式 ]] 支持正则 =~
特别说明:
(1)[ 亲亲,我两边都有空格,不空打死你呦 ]
(2)[[ 亲亲,我两边都有空格,不空打死你呦 ]]
(3)更多判断,man test
去查看,很多参数都用来进行条件判断
2、条件判断相关参数
问:你要判断什么?
答:我要判断文件类型,判断文件新旧,判断字符串是否相等、判断权限等等。。。
(1)判断文件类型
[root@Nchu150412 a]# ls
first.sh ip.txt variable.sh
[root@Nchu150412 a]# touch file1
[root@Nchu150412 a]# ls
file1 first.sh ip.txt variable.sh
[root@Nchu150412 a]# echo hello > file2
[root@Nchu150412 a]# ls
file1 file2 first.sh ip.txt variable.sh
[root@Nchu150412 a]# ls
file1 file2 first.sh ip.txt variable.sh
[root@Nchu150412 a]# mkdir dir1
[root@Nchu150412 a]# test -e ./file1
[root@Nchu150412 a]# echo $?
0
[root@Nchu150412 a]# test -e ./test1 #只要文件存在,条件为真
[root@Nchu150412 a]# echo $?
1
[root@Nchu150412 a]# ll
总用量 20
drwxr-xr-x. 2 root root 4096 9月 28 09:33 dir1
-rw-r--r--. 1 root root 0 9月 28 09:32 file1
-rw-r--r--. 1 root root 6 9月 28 09:32 file2
-rwxr-xr-x. 1 root root 207 9月 23 16:50 first.sh
-rw-r--r--. 1 root root 9 9月 26 10:55 ip.txt
-rwxr-xr-x. 1 root root 179 9月 27 14:22 variable.sh
[root@Nchu150412 a]# [ -d ./dir1 ];echo $? #判断目录是否存在,存在条件为真
0
[root@Nchu150412 a]# ln -s file1 test1
[root@Nchu150412 a]# ll
总用量 20
drwxr-xr-x. 2 root root 4096 9月 28 09:33 dir1
-rw-r--r--. 1 root root 0 9月 28 09:32 file1
-rw-r--r--. 1 root root 6 9月 28 09:32 file2
-rwxr-xr-x. 1 root root 207 9月 23 16:50 first.sh
-rw-r--r--. 1 root root 9 9月 26 10:55 ip.txt
lrwxrwxrwx. 1 root root 5 9月 28 09:38 test1 -> file1
-rwxr-xr-x. 1 root root 179 9月 27 14:22 variable.sh
[root@Nchu150412 a]# [ -L ./test1 ];echo $?
0
[root@Nchu150412 a]# rm -f file1
[root@Nchu150412 a]# ll
总用量 20
drwxr-xr-x. 2 root root 4096 9月 28 09:33 dir1
-rw-r--r--. 1 root root 6 9月 28 09:32 file2
-rwxr-xr-x. 1 root root 207 9月 23 16:50 first.sh
-rw-r--r--. 1 root root 9 9月 26 10:55 ip.txt
lrwxrwxrwx. 1 root root 5 9月 28 09:38 test1 -> file1
-rwxr-xr-x. 1 root root 179 9月 27 14:22 variable.sh
[root@Nchu150412 a]# [ -L ./test1 ];echo $?
0
[root@Nchu150412 a]# [[ -f ./file2 ]];echo $? #判断文件是否存在,并且是一个普通的文件
0
[root@Nchu150412 a]# [[ -f ./test1 ]];echo $?
1
[root@Nchu150412 a]# [ ! -d ./dir1 ];echo $? #判断目录是否存在,不存在条件为真
1
[root@Nchu150412 a]# [ ! -d ./dir2 ];echo $?
0
(2)判断文件权限
[root@Nchu150412 a]# ll
总用量 20
drwxr-xr-x. 2 root root 4096 9月 28 09:33 dir1
-rw-r--r--. 1 root root 6 9月 28 09:32 file2
-rwxr-xr-x. 1 root root 207 9月 23 16:50 first.sh
-rw-r--r--. 1 root root 9 9月 26 10:55 ip.txt
lrwxrwxrwx. 1 root root 5 9月 28 09:38 test1 -> file1
-rwxr-xr-x. 1 root root 179 9月 27 14:22 variable.sh
[root@Nchu150412 a]# test -r first.sh ;echo $?
0
(3)判断文件新旧
说明
:这里的新旧指的是文件的修改时间
diff
命令用于比较文件的差异。
diff
以逐行的方式,比较文本文件的异同处。如果指定要比较目录,则 diff 会比较目录中相同文件名的文件,但不会比较其中子目录。
[root@Nchu150412 a]# ll
总用量 20
drwxr-xr-x. 2 root root 4096 9月 28 09:33 dir1
-rw-r--r--. 1 root root 0 9月 28 10:36 file1
-rw-r--r--. 1 root root 6 9月 28 09:32 file2
-rwxr-xr-x. 1 root root 207 9月 23 16:50 first.sh
-rw-r--r--. 1 root root 9 9月 26 10:55 ip.txt
lrwxrwxrwx. 1 root root 5 9月 28 09:38 test1 -> file1
-rwxr-xr-x. 1 root root 179 9月 27 14:22 variable.sh
[root@Nchu150412 a]# test file1 -nt file2;echo $?
0
[root@Nchu150412 a]# test file1 -ot file2;echo $?
1
[root@Nchu150412 a]# diff file1 file2
0a1
> hello
[root@Nchu150412 a]# cat file1
[root@Nchu150412 a]# cat file2
hello
[root@Nchu150412 a]# echo hello > file1
[root@Nchu150412 a]# cat file1
hello
[root@Nchu150412 a]# diff file1 file2
[root@Nchu150412 a]# [ file1 -ef file2 ];echo $?
1
(4)判断整数
[root@Nchu150412 a]# [ 1 -eq 2 ];echo $?
1
[root@Nchu150412 a]# [ 1 -ne 2 ];echo $?
0
[root@Nchu150412 a]# [ 1 -le 2 ];echo $?
0
(5)判断字符串
[root@Nchu150412 a]# test -z "hello world";echo $?
1
[root@Nchu150412 a]# test -n "hello world";echo $?
0
[root@Nchu150412 a]# test -n " ";echo $?
0
[root@Nchu150412 a]# test -n "";echo $?
1
[root@Nchu150412 a]# test -z "";echo $?
0
[root@Nchu150412 a]# test "hello" = "world";echo $?
1
[root@Nchu150412 a]# test "hello" != "world";echo $?
0
(6)多重条件判断
特别说明:
&&
前面的表达式为真,才会执行后面的代码
||
前面的表达式为假,才会执行后面的代码
;
只用于分割命令或表达式
数值比较:
[root@Nchu150412 a]# id -u
0
[root@Nchu150412 a]# [ $(id -u) -eq 0 ]&& echo "admin"
admin
[root@Nchu150412 a]# su - syt
上一次登录:三 9月 28 09:30:54 CST 2022:0 上
[syt@Nchu150412 ~]$ [ $(id -u) -eq 0 ]&& echo "admin"
[syt@Nchu150412 ~]$ id -u
1000
[syt@Nchu150412 ~]$ [ $(id -u) -eq 0 ]|| echo "is not admin"
is not admin
[syt@Nchu150412 ~]$ [ $(id -u) -eq 0 ]&& echo "admin" || echo "is not admin"
is not admin
[syt@Nchu150412 ~]$ su -
密码:
上一次登录:三 9月 28 09:31:33 CST 2022pts/0 上
[root@Nchu150412 ~]# [ $(id -u) -eq 0 ]&& echo "admin" || echo "is not admin"
admin
[root@Nchu150412 ~]# [ 1 -eq 2 ]|| echo AAA && echo BBB && echo CCC
AAA
BBB
CCC
[root@Nchu150412 ~]# [ 1 -eq 2 ]&& echo AAA || echo BBB && echo CCC
BBB
CCC
类C风格的数值比较:
注意:在(())中,=表示赋值;==表示判断
[root@Nchu150412 ~]# ((1==1));echo $?
0
[root@Nchu150412 ~]# (( 1== 1));echo $?
0
[root@Nchu150412 ~]# (( 1>= 1));echo $?
0
[root@Nchu150412 ~]# (( 1>= 2));echo $?
1
[root@Nchu150412 ~]# (( 1= 2));echo $?
-bash: ((: 1= 2: 尝试给非变量赋值 (错误符号是 "= 2")
1
[root@Nchu150412 ~]# (( A1= 2));echo $?
0
[root@Nchu150412 ~]# echo $A1
2
[root@Nchu150412 ~]# (( 1!== 2));echo $?
-bash: ((: 1!== 2: 语法错误: 期待操作数 (错误符号是 "= 2")
1
[root@Nchu150412 ~]# (( 1!= 2));echo $?
0
[root@Nchu150412 ~]# (( 1> 2));echo $?
1
字符串比较:
注意:双引号引起来,看作一个整体;= 和 == 在 [ 字符串 ] 比较中都表示判断
[root@Nchu150412 ~]# A=hello;B=world;[ "$A" = "$B" ];echo $?
1
[root@Nchu150412 ~]# A=hello;B=world;[ "$A" != "$B" ];echo $?
0
[root@Nchu150412 ~]# A=hello;B=world;[ "$A" == "$B" ];echo $?
1
注:字符串判断一定要加双引号。
思考:[]和[[]]有什么区别?
[root@Nchu150412 ~]# A=
[root@Nchu150412 ~]# echo $A
[root@Nchu150412 ~]# test $A = hello
-bash: test: =: 期待一元表达式
[root@Nchu150412 ~]# test $A = "hello"
-bash: test: =: 期待一元表达式
[root@Nchu150412 ~]# test "$A" = "hello"
[root@Nchu150412 ~]# echo $?
1
[root@Nchu150412 ~]# [ $A = hello ];echo $?
-bash: [: =: 期待一元表达式
2
[root@Nchu150412 ~]# [ "$A" = "hello" ];echo $?
1
[root@Nchu150412 ~]# [[ $A = hello ]];echo $?
1
注:判断空字符串时,[[]]不加双引号也不会报错,而[]两侧不加双引号会报错,因为$A为空,就相当于拿[与hello做对比,那语法上就会少一个[,所以会报错。
[root@Nchu150412 a]# ll
总用量 24
drwxr-xr-x. 2 root root 4096 9月 28 09:33 dir1
-rw-r--r--. 1 root root 6 9月 28 10:40 file1
-rw-r--r--. 1 root root 6 9月 28 09:32 file2
-rwxr-xr-x. 1 root root 207 9月 23 16:50 first.sh
-rw-r--r--. 1 root root 9 9月 26 10:55 ip.txt
lrwxrwxrwx. 1 root root 5 9月 28 09:38 test1 -> file1
-rwxr-xr-x. 1 root root 179 9月 27 14:22 variable.sh
[root@Nchu150412 a]# rm -f file1
[root@Nchu150412 a]# ll
总用量 20
drwxr-xr-x. 2 root root 4096 9月 28 09:33 dir1
-rw-r--r--. 1 root root 6 9月 28 09:32 file2
-rwxr-xr-x. 1 root root 207 9月 23 16:50 first.sh
-rw-r--r--. 1 root root 9 9月 26 10:55 ip.txt
lrwxrwxrwx. 1 root root 5 9月 28 09:38 test1 -> file1
-rwxr-xr-x. 1 root root 179 9月 27 14:22 variable.sh
[root@Nchu150412 a]# [ -e test1 -a -L test1 ];echo $?
1
[root@Nchu150412 a]# touch file1
[root@Nchu150412 a]# ll
总用量 20
drwxr-xr-x. 2 root root 4096 9月 28 09:33 dir1
-rw-r--r--. 1 root root 0 9月 28 16:54 file1
-rw-r--r--. 1 root root 6 9月 28 09:32 file2
-rwxr-xr-x. 1 root root 207 9月 23 16:50 first.sh
-rw-r--r--. 1 root root 9 9月 26 10:55 ip.txt
lrwxrwxrwx. 1 root root 5 9月 28 09:38 test1 -> file1
-rwxr-xr-x. 1 root root 179 9月 27 14:22 variable.sh
[root@Nchu150412 a]# [ -e test1 -a -L test1 ];echo $?
0
[root@Nchu150412 a]# [[ -e test1 && -L test1 ]];echo $? #想写在里面就用[[]]
0
[root@Nchu150412 a]# [ -e test1 ]&&[ -L test1 ];echo $?
0
逻辑运算符总结:
1、符号;
和&&
和||
都可以用来分割命令或者表达式
2、;
完全不考虑前面的语句是否正确执行,都会执行;
号后面的内容
3、&&
符号,需要考虑&&
前面的语句的正确性,前面语句正确执行才会执行&&
后的内容;反之亦然
4、||
符号,需要考虑||
前面的语句的非正确性,前面语句执行错误才会执行||
后内容;反之亦然
5、如果&&
和||
一起出现,从左往右依次看,按照以上原则。
四、流程控制语句
1、基本语法结构
(1)if结构
if [ 条件 ];then
命令
fi
if test 条件;then
命令
fi
if [[ 条件 ]];then
命令
fi
[ 条件 ] && 命令
(2)if…else结构
if [ 条件 ];then
命令
else
命令
fi
[ 条件 ] && 命令 || 命令
小试牛刀:
让用户自己输入字符串,如果用户输入的是hello,请打印world,否则打印“请输入hello”
#!/bin/env bash
read -p "请输入一个字符串:" str
if [ "str" = "hello" ]
then
echo world
else
echo "请输入hello!"
fi
--------------------------------------------------------------------------------------------------
#!/bin/env bash
echo "该脚本需要传递参数"
if [[ $1 = hello ]];then
echo "hello"
else
echo "请输入hello"
fi
--------------------------------------------------------------------------------------------------
#!/bin/env bash
A=hello
B=world
C=hello
if [ "$1" = "$A" ];then
echo "$B"
else
echo "$C"
fi
--------------------------------------------------------------------------------------------------
#!/bin/env bash
read -p "请输入一个字符串:" str;
[ "$str" = "hello" ] && echo "world" || echo "请输入hello!"
(3)if …elif…else结构
if [ 条件1 ];then
命令1 结束
elif [ 条件2 ];then
命令2 结束
else
命令3
fi
注释:
如果条件1满足,执行命令1后结束;如果条件1不满足,再看条件2,如果条件2满足执行命令2后结束;如果条件1和条件2
都不满足执行命令3结束。
bash -x if2.sh
#可以看代码怎么运行的
(4)层层嵌套结构
if [ 条件1 ];then
命令
if [ 条件2 ];then
命令2
fi
else
if [ 条件3 ];then
命令3
elif [ 条件4 ];then
命令4
else
命令5
fi
fi
应用案例
(1)判断两台主机是否ping通
需求:判断当前主机和远程主机是否ping通
思路:
1、使用哪个命令实现 ping -c次数
2、根据命令的执行结果状态来判断是否通 $?
3、根据逻辑和语法结构来编写脚本(条件判断或者流程控制)
落地实现:
#!/bin/env bash
# 该脚本用于判断当前主机是否和远程指定主机互通
# 交互式定义变量,让用户自己决定ping哪个主机
read -p "请输入你要ping的主机的IP:" ip
# 使用ping程序判断主机是否互联
ping -c1 $ip &>/dev/null
if [ $? -eq 0 ];then
echo "当前主机和远程主机$ip是互通的"
else
echo "当前主机和远程主机$ip不通的"
fi
(2)判断一个进程是否存在
需求:判断web服务器中httpd进程是否存在
思路:
1、查看进程的相关命令
2、根据命令的返回状态值来判断进程是否存在
3、根据逻辑用脚本语言实现
落地实现
#!/bin/env bash
# 判断一个程序(httpd)的进程是否存在
pgrep httpd &>/dev/null
if [ $? -ne 0 ];then
echo "当前httpd进程不存在"
else
echo "当前httpd进程存在"
fi
或者
test $? -eq 0 && echo "当前httpd进程存在" || echo "当前httpd进程不存在"
补充命令
pgrep命令:以名称为依据从运行进程队列中查找进程,并显示查找到的进程id
选项
-o:仅显示找到的最小(起始)进程号
-n:仅显示找到的最大(结束)进程号
-l:显示进程名字
-p:指定父进程号;pgrep -p 4764 查看父进程下的
(3)判断一个服务是否正常
需求:判断门户网站是否能够正常访问
思路:
1、可以判断进程是否存在,用/etc/init.d/httpd status
判断状态等方法
2、最好的方法是直接去访问一下,通过访问成功和失败的返回值来判断
落地实现
#!/bin/env bash
# 判断门户网站是否能够正常提供服务
#定义变量
web_server=www.itcast.cn
#访问网站
wget -P /shell/ $web_server &>/dev/null
[ $? -eq 0 ] && echo "当前网站服务是ok" && rm -f /shell/index.* || echo "当前网站服务不ok,请立刻处理"
(4)判断用户是否存在
需求1:输入一个用户,用脚本判断该用户是否存在
#!/bin/env bash
read -p "请输入一个用户名:" user_name
id $user_name &>/dev/null
if [ $? -eq 0 ];then
echo "该用户存在!"
else
echo "用户不存在!"
fi
五、for循环语句
1、for循环语法结构
(一) 列表循环
列表for循环:用于将一组命令执行已知的次数
基本语法格式
for variable in {list}
do
命令
命令
...
done
或者
for variable in a b c
do
命令
命令
...
done
#!/bin/env bash
for i in {1..5}
do
echo hello world
done
或者
for i in a b c d e
do
echo hello world
done
#打印1-50间的偶数
#!/bin/env bash
for i in {0..50..2}
do
echo $i
done
for i in {10..1};do echo $i;done
for i in {10..1..-2};do echo $i;done
for i in ‘seq 10 -2 1’;do echo $i;done
(二) 不带列表循环
不带列表的for循环执行时由用户指定参数和参数的个数
基本语法格式
for i
do
命令
命令
...
done
(三) 类C风格的for循环
基本语法结构
for(( expri;expr2;expr3 ))
do
命令
命令
...
done
for (( i=1;i<=5;i++ ))
do
echo $i
done
expr1:定义变量并赋初值
expr2:决定是否进行循环(条件)
expr3:决定循环变量如何改变,改变循环什么时候退出
for ((i=1;i<=5;i++));do echo $i;done
for ((i=1;i<=10;i+=2));do echo $i;done
for ((i=2;i<=10;i+-2));do echo $i;done
2、应用案例
(一)脚本计算1-100奇数和
思路:
1、定义一个变量来保存奇数的和
2、找出1-100的奇数,保存到另一个变量里
3、从1-100中找出奇数后,再相加,然后将和赋值给变量
4、遍历完毕后,将sum的值打印出来
落地实现:
#!/bin/env bash
# 计算1-100的奇数和
# 定义变量来保存奇数和
sum=0
#for循环遍历1-100的奇数,并且相加,把结果重新赋值给sum
for i in {1..100..2}
do
let sum=$sum+$i
done
#打印所有奇数的和
echo "1-100的奇数和是:$sum"
-------------------------------------------------------------------------------------------------
#!/bin/env bash
sum=0
for((i=1;i<=100;i++))
do
if [ $[$i%2] -ne 0 ];then
let sum=$sum+$i
fi
或者
test $[$i%2] -ne 0 && let sum=$sum+$i
done
echo "1-100的奇数和为:$sum"
-------------------------------------------------------------------------------------------------
(二)判断所输整数是否为质数
质数(素数):只能被1和它本身整除的数叫质数。
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
思路:
1、让用户输入一个数,保存到一个变量里 read -p "请输入一个正整数:" num
2、如果能被其他数整除就不是质数 $num%$i是否等于0 $i=2到$num-1
3、如果输入的数是1或者2取模,根据上面判断又不符合,所以先排除1和2
4、测试序列从2开始,输入的数是4 得出结果$num不能和$i相等,并且$num不能小于$i
落地实现:
#!/bin/env bash
#定义变量来保存用户所输入数字
read -p "请输入一个正整数字:" number
#先排除用户输入的数字1和2
[ $number -eq 1 ] && echo "$number不是质数" && exit
[ $number -eq 2 ] && echo "$number是质数" && exit
#循环判断用户所输入的数字是否质数
for i in 'seq 2 $[ $number-1 ]'
do
[ $[$number%$i] -eq 0 ] && echo "$number不是质数" && exit
done
echo "$number是质数" && exit
(三) 批量创建用户
需求:批量加5个新用户,以u1到u2命名,并统一加一个新组,组名为class,统一改密码为123
思路:
1、添加用户命令 useradd -G class
2、判断class组是否存在 grep -w ^class /etc/group 或者groupadd class
3、根据题意,判断该脚本循环5次来添加用户 for
4、给用户设置密码,应该放到循环体里面
落地实现:
#!/bin/env bash
#判断class组是否存在
grep -w ^class /etc/group &>/dev/null
test $? -ne 0 && groupadd class
#循环创建用户
for((i=1;i<=5;i++))
do
useradd -G class u$i
echo 123|passwd --stdin u$i
done
3、课堂练习
(一)批量创建用户
需求:批量新建5个用户stu1~stu5,要求这几个用户的家目录都在/rhome
#!/bin/env bash
#判断/rhome是否存在
[ -f /rhome ] && mv /rhome /rhome.bak
test ! -d /rhome && mkdir /rhome
或者
[ -f /rhome ] && mv /rhome /rhome.bak || [ ! -d /rhome ] && mkdir /rhome
#创建用户,循环5次
for((i=1;i<=5;i++))
do
useradd -d /rhome/stu$i stu$i
echo 123|passwd --stdin stu$i
done
(二)局域网内脚本检查主机网络通讯
需求:
写一个脚本,局域网内,把能ping通的ip和不能ping通的ip分类,并保存到两个文本文件里
以10.1.1.1-10.1.1.10为例
#!/bin/env bash
#定义变量
ip=10.1.1
#循环去ping主机的IP
for((i=1;i<=10;i++))
do
ping -c1 $ip.$i &>/dev/null
if [$? -eq 0 ];then
echo "$ip.$i is ok" >> /tmp/ip_up.txt
else
echo "$ip.$i is down" >> /tmp/ip_down.txt
fi
或者
[ $? -eq 0 ] && echo "$ip.$i is ok" >> /tmp/ip_up.txt || echo "$ip.$i is down" >> /tmp/ip down.txt
done
shell脚本并发
并行执行:
{程序}&表示将程序放到后台并行执行,如果需要等待程序执行完毕再进行下面内容,需要加wait
#!/bin/bash
#定义变量
ip=10.1.1
#循环去ping主机的IP
for((i=1;i<=10;i++))
do
{
ping -c1 $ip,$i &>/dev/null
if [$? -eq 0 ];then
echo "$ip.$i is ok" >> /tmp/ip_up.txt
else
echo "$ip.$i is down" >> /tmp/ip_down.txt
fi
}&
done
wait
echo "ip is ok..."
continue
:继续;表示循环体内下面的代码不执行,跳过本次循环,重新开始下一次循环
break
:打断;马上跳出循环,执行循环体外的代码
exit
:退出,直接跳出程序
六、while循环语句
特点:条件为真就进入循环;条件为假就退出循环
1、while循环语法结构
while 表达式
do
命令
done
while [ 1 -eq 1 ] 或者 (( 1 > 2 ))
do
命令
命令
...
done
for((i=1;i<=5;i++));do echo $i;done
i=1;while [ $i -le 5 ];do echo $i;let i++;done
-------------------------------------------------------------------------------------------------
for((i=1;i<=10;i+=2));do echo $i;done
i=1;while (( $i <= 10 ));do echo $i;let i+=2;done
(一)脚本计算1-50偶数和
#!/bin/env bash
sum=0
for((i=0;i<=50;i+=2))
do
let sum=$sum+$i (let sum=sum+i)
done
echo "1-50的偶数和为:$sum"
#!/bin/env bash
#定义变量
sum=0
i=2
#循环打印1-50的偶数和并且计算后重新赋值给sum
while [ $i -le 50 ]
do
let sum=sum+i
let i+=2
done
#打印sum的值
echo "1-50的偶数和为:$sum"
(二)脚本同步系统时间
具体需求:
1、写一个脚本,30秒同步一次系统时间,时间同步服务器10.1.1.1
2、如果同步失败,则进行邮件报警,每次失败都报警
3、同步成功,也进行邮件通知,但是成功100次才通知一次
思路:
1、每个30s同步一次时间,该脚本是一个死循环
2、同步失败发送邮件
3、同步成功100次发送邮件
mail -s “邮件主题”
#!/bin/env bash
#该脚本用于时间同步
NTP=10.1.1.1
count=0
while true
do
ntpdate $NTP &>/dev/null
if [ $? -ne 0 ];then
echo "system date failed" |mail -s "check system date" root@localhost
else
let count++
if [ $count -eq 100 ];then
echo "system date success" |mail -s "check system date" root@localhost && count=0
fi
fi
sleep 30
done
七、while循环语句
特点:条件为假就进入循环;条件为真就退出循环
1、until语法结构
until expression [ 1 -eq 1 ] (( 1>=1 ))
do
command
command
...
done
2、应用案例
(一)具体需求
1、使用until语句批量创建10个用户,要求stu1-stu5用户的UID分别为1001-1005;
2、stu6-stu10用户的家目录分别在/rhome/stu6-/rhome/stu10
(二)思路
1、创建用户语句 useradd -u|useradd -d
2、使用循环语句(until)批量创建用户 until循环语句结构
3、判断用户前5个和后5个 条件判断语句
(三)落地实现
#!/bin/env bash
#[ -d /rhome ] &>/dev/null
if [ -d /rhome ]
then
echo "/rhome目录已存在"
else
mkdir /rhome
echo "/rhome不存在,已完成创建"
fi
i=1
until [ $i -gt 5 ]
do
#useradd stu$i -u 100$i
useradd stu$i -u $[1000+$i]
let i++
done
until [ $i -gt 10 ]
do
useradd stu$i -d /rhome/stu$i
let i++
done
改进代码:
#!/bin/env bash
#[ -d /rhome ] &>/dev/null
if [ -d /rhome ]
then
echo "/rhome目录已存在"
else
mkdir /rhome
echo "/rhome不存在,已完成创建"
fi
i=1
until [ $i -gt 10 ]
do
if [ $i -le 5 ];then
useradd -u $[1000+$i] stu$i
echo 123|passwd --stdin stu$i
else
useradd -d /rhome/stu$i stu$i
echo 123|passwd --stdin stu$i
fi
let i++
done
八、随机数
1、如何生成随机数?
系统变量:RANDOM,默认会产生0~32767的随机整数
前言:要想调用变量,不管你是什么变量都要给钱,并且是美元$
打印一个随机数
echo $RANDOM
查看系统上一次生成的随机数
# set|grep RANDOM
RANDOM=28325
-------------------------------------------------------------------------------------------------
产生0-1之间的随机数
echo $[ $RANDOM%2 ]
产生0-2之间的随机数
echo $[ $RANDOM%3 ]
产生0-3之间的随机数
echo $[ $RANDOM%4 ]
......
产生0-9之间的随机数
echo $[ $RANDOM%10 ]
产生0-100之间的随机数
echo $[ $RANDOM%101 ]
产生50-100之间的随机数
echo $[ $RANDOM%51+50 ]
产生1-50之间的随机数
echo $[ $RANDOM%50+1 ]
产生10-99之间的随机数
echo $[ $RANDOM%90+10 ]
产生100-999之间的随机数(产生三位数的随机数)
echo $[ $RANDOM%900+100 ]
(一)随机产生以139开头的电话号码
需求:
写一个脚本,产生一个phonenum.txt文件,随机产生以139开头的手机号1000个,每个一行。
思路:
1、产生1000个电话号码,脚本需要循环1000次
2、139+8位,后8位随机产生,可以让每一位数字都随机产生
3、将随机产生的数字分别保存到变量里,然后加上139保存到文件里
落地实现
#!/bin/env bash
#产生1000个以139开头的电话号码并保存文件phonenum.txt
file=/shell03/phonenum.txt
for((i=1;i<=1000;i++))
do
n1=$[ $RANDOM%10 ]
n2=$[ $RANDOM%10 ]
n3=$[ $RANDOM%10 ]
n4=$[ $RANDOM%10 ]
n5=$[ $RANDOM%10 ]
n6=$[ $RANDOM%10 ]
n7=$[ $RANDOM%10 ]
n8=$[ $RANDOM%10 ]
echo "139$n1$n2$n3$n4$n5$n6$n7$n8" >> $file
done
(二)随机抽出5位幸运观众
具体需求:
1、在上面的1000个手机号里抽奖5个幸运观众,显示出这5个幸运观众。
2、但只显示头3个数和尾号的4个数,中间的都用*代替
思路:
1、确定幸运观众所在的行 1~1000 随机找出一个数字 $[$RANDOM%1000+1]
2、将电话号码提取出来 head -随机产生行号 phonenum.txt |tail -1
3、显示前3个和后4个数到屏幕
落地实现:
#!/bin/env bash
#定义变量
phone=/shell03/phonenum.txt
#循环抽出5位幸运观众
for ((i=1;i<=5;i++))
do
#定位幸运观众所在行号
line=`wc -l $phone |cut -d' ' -f1` #wc -l 统计文本输出的行数
luck_line=$[RANDOM%$line+1]
#取出幸运观众所在行的电话号码
luck_num=`head -$luck_line $phone|tail -1` #head是指取前n行,tail是指取最后m行
#显示到屏幕
echo "139****${luck_num:7:4}"
echo $luck_num >> luck.txt
#删除已经被抽取的幸运观众号码
sed -i "/$luck_num/d" $phone
done
(三)批量创建用户(密码随机产生)
**需求:**批量创建5个用户,每个用户的密码为一个随机数
思路:
1、循环5次创建用户
2、产生一个密码文件来保存用户的随机密码
3、从密码文件中取出随机密码赋值给用户
落地实现:
#!/bin/env bash
#crate user and set passwd
#产生一个保存用户名和密码的文件
echo user0{1..5}:itcast$[$RANDOM%9000+1000]#@~|tr ' ' '\n' >> user_pass.file
#循环创建5个用户
for ((i=1;i<=5;i++))
do
user=`head -$i user_pass.file|tail -1|cut -d: -f1` #-d用来定义分隔符,使用‘:’分隔,-f表示需要取得哪个字段
pass=`head -$i user_pass.file|tail -1|cut -d: -f2`
useradd $user
echo $pass|passwd --stdin $user
done
或者
for i in `cat user_pass.file`
do
user=`echo $i|cut -d: -f1`
pass=`echo $i|cut -d: -f2`
useradd $user
echo $pass|passwd --stdin $user
done
九、嵌套循环
循环嵌套就是外层循环一次,内层循环一轮。
1、一个循环体内又包含另一个完整的循环结构,称为循环的嵌套。
2、每次外部循环都会触发内部循环,直至内部循环完成,才接着执行下一次的外部循环。
3、for循环、while循环和until循环可以相互嵌套。
1、应用案例
(一)打印指定图案
#!/bin/env bash
for((y=1;y<=5;y++))
do
for((i=5;i>5-y;i--))
do
echo -n $i
done
echo
done
-------------------------------------------------------------------------------------------------
5
54
543
5432
54321
shift
:使位置参数向左移动,默认移动1位,可以使用shift 2
(向左移一位,原本第二个就成第一个了)
举例说明:
以下脚本都能够实现用户自定义输入数字,然后脚本计算和:
#!/bin/env bash
sum=0
while [ $# -ne 0 ]
do
let sum=$sum+$1 #$1是第一位
shift
done
echo sum=$sum
-------------------------------------------------------------------------------------------------
#!/bin/env bash
sum=0
for i
do
let sum=$sum+$i
done
echo sum=$sum
-------------------------------------------------------------------------------------------------
[syt@Nchu150412 shell]$ bash -x ./for6.sh 7 8 9
+ sum=0
+ '[' 3 -ne 0 ']'
+ let sum=0+7
+ shift
+ '[' 2 -ne 0 ']'
+ let sum=7+8
+ shift
+ '[' 1 -ne 0 ']'
+ let sum=15+9
+ shift
+ '[' 0 -ne 0 ']'
+ echo sum=24
sum=24
2、补充扩展expect
(一)概述
使用Linux
的朋友对输入密码都不会陌生,比如使用超级用户执行命令,又比如scp
、ssh
连接远程主机等等。如果我们脚本里面有scp
的操作,总不可能执行一次scp就输入密码一次,这样就需要一个人盯着脚本运行了。 为了解决这个问题,我们需要一个自动输入密码的功能。
expect
是建立在tcl
基础上的一个工具,它可以让一些需要交互的任务自动化地完成。相当于模拟了用户和命令行的交互操作,实现自动应答。
一个具体的场景:远程登陆服务器,并执行命令。
(二)expect是什么?
expect
是一个免费的编程工具,用来实现自动的交互式任务,而无需人为干预。说白了,expect
就是一套用来实现自动交互功能的软件。
在实际工作中,我们运行命令、脚本或程序时,这些命令、脚本或程序都需要从终端输入某些继续运行的指令,而这些输入都需要人为的手工进行。而利用expect
,则可以根据程序的提示,模拟标准输入提供给程序,从而实现自动化交互执行。这就是expect
!!!
(三)expect基础
在使用expect
时,基本上都是和以下四个命令打交道:
命令 | 作用 |
---|---|
send | 用于向进程发送字符串 |
expect | 从进程接收字符串 |
spawn | 启动新的进程 |
interact | 允许用户交互 |
send
命令接收一个字符串参数,并将该参数发送到进程。
expect
命令和send
命令相反,expect
通常用来等待一个进程的反馈,我们根据进程的反馈,再发送对应的交互命令。
spawn
命令用来启动新的进程,spawn
后的send
和expect
命令都是和使用spawn
打开进程进行交互。
interact
命令用的其实不是很多,一般情况下使用spawn
、send
和expect
命令就可以很好的完成我们的任务;但在一些特殊场合下还是需要使用interact
命令的,interact
命令主要用于退出自动化,进入人工交互。比如我们使用spawn
、send
和expect
命令完成了ftp
登陆主机,执行下载文件任务,但是我们希望在文件下载结束以后,仍然可以停留在ftp
命令行状态,以便手动的执行后续命令,此时使用interact
命令就可以很好的完成这个任务。
结束符
expect eof
:等待执行结束,若没有这一句,可能导致命令还没执行,脚本就结束了
interact
: 执行完成后保持交互状态, 这时可以手动输入信息
注:expect eof 与 interact 二选一即可
需求1:A远程登陆到server上什么都不做
#没有没关系,看下一个
#!/usr/bin/env expect
#开启一个程序
spawn ssh root@10.1.1.1 #它主要的功能是给ssh运行进程加个壳,用来传递交互指令;
#捕获相关内容
expect {
"(yes/no)?" { send "yes\r";exp_continue } #有exp_continue则可以不断循环匹配,输入多条命令,简化写法。
"password:" { send "123456\r" }
}
interact #交互
脚本执行方式
#./expect1.sh
#/shell04/expect1.sh
#expect -f expect1.sh
使用位置参数
#!/usr/bin/expect
set ip [ lindex $argv 0 ]
set pass [ lindex $argv 1 ]
set timeout 5
spawn ssh root@$ip
expect {
"yes/no" { send "yes\r";exp_continue }
"password:" { send "$pass\r" }
}
interact
需求2:A远程登陆到server上操作
#!/usr/bin/expect
set ip 192.168.18.144
set pass Nchu150401
set timeout 5
spawn ssh syt@$ip
expect {
"yes/no" { send "yes\r";exp_continue } #有exp_continue则可以不断循环匹配,输入多条命令,简化写法。
"password:" { send "$pass\r" }
}
expect "#" #也可以用expect {"#"}
send "rm -rf /tmp/*\r"
send "touch /tmp/file{1..3}\r"
send "date\r"
send "exit\r"
expect eof
需求3:shell脚本和expect结合使用,在多台服务器上创建1个用户
[root@server shell04]# cat ip.txt
10.1.1.1 123456
10.1.1.2 123456
1. 循环
2. 登录远程主机——>ssh——>从ip.txt文件里获取IP和密码分别赋值给两个变量
3. 使用expect程序来解决交互问题
#!/bin/bash
# 循环在指定的服务器上创建用户和文件
while read ip pass
do
/usr/bin/expect <<-END &>/dev/null
spawn ssh root@$ip
expect {
"yes/no" { send "yes\r";exp_continue }
"password:" { send "$pass\r" }
}
expect "#" { send "useradd yy1;rm -rf /tmp/*;exit\r" }
expect eof
END
done < ip.txt
#!/bin/bash
cat ip.txt|while read ip pass
do
{
/usr/bin/expect <<-HOU
spawn ssh root@$ip
expect {
"yes/no" { send "yes\r";exp_continue }
"password:" { send "$pass\r" }
}
expect "#"
send "hostname\r"
send "exit\r"
expect eof
HOU
}&
done
wait
echo "user is ok...."
或者
#!/bin/bash
while read ip pass
do
{
/usr/bin/expect <<-HOU
spawn ssh root@$ip
expect {
"yes/no" { send "yes\r";exp_continue }
"password:" { send "$pass\r" }
}
expect "#"
send "hostname\r"
send "exit\r"
expect eof
HOU
}&
done<ip.txt
wait
echo "user is ok...."
(四)综合案例
实战案例1
(一)具体需求
写一个脚本,将跳板机上yunwei用户的公钥推送到局域网内可以ping通的所有机器上
说明:主机和密码文件已经提供
10.1.1.1:123456
10.1.1.2:123456
(二)案例分析
关闭防火墙和selinux
判断ssh服务是否开启(默认ok)
循环判断给定密码文件里的哪些IP是可以ping通
判断IP是否可以ping通——>$?—>流程控制语句
密码文件里获取主机的IP和密码保存变量
判断公钥是否存在—>不存在创建它
ssh-copy-id 将跳板机上的yunwei用户的公钥推送到远程主机—>expect解决交互
将ping通的主机IP单独保存到一个文件
测试验证
(三)落地实现
① 代码拆分
1.判断yunwei用户的公钥是否存在
[ ! -f /hoem/yunwei/.ssh/id_rsa ] && ssh-keygen -P '' -f ./id_rsa
2.获取IP并且判断是否可以ping通
1)主机密码文件ip.txt
10.1.1.1:123456
10.1.1.2:123456
2) 循环判断主机是否ping通
tr ':' ' ' < ip.txt|while read ip pass
do
ping -c1 $ip &>/dev/null
if [ $? -eq 0 ];then
推送公钥
fi
done
3.非交互式推送公钥
/usr/bin/expect <<-END &>/dev/null
spawn ssh-copy-id root@$ip
expect {
"yes/no" { send "yes\r";exp_continue }
"password:" { send "$pass\r" }
}
expect eof
END
十、Shell脚本数组和其他变量
数组定义
(一)数组分类
普通数组:只能使用整数作为数组索引(元素的下标)
关联数组:可以使用字符串作为数组索引(元素的下标)
(二)普通数组定义
数组名[索引下标]=值
array[0]=n1
array[1]=n2
array[2]=n3
数组名=(值1 值2 值3 ...)
array=(n1 n2 n3 ...)
array1=(`cat /etc/passwd`) #将文件中每一行赋值给array1数组
array2=(1 2 3 "hello world" [10]=linux)
(三)数组的读取
${数组名[元素下标]}
echo ${array[0]} 读取数组第一个元素
echo ${array[*]} 读取数组所有元素
echo ${#array[*]} 读取数组所有元素个数
echo ${!array[*]} 读取数组元素的索引下标
echo ${array[*]:1:2} 读取下标为1的元素开始,获取2个元素
declare -a 查看普通数组信息
2、关联数组:使用字符串作为数组索引(元素下标)
declare -A abc_array1 首先声明关联数组
数组名[索引or下标]=变量值 数组赋值
一次赋一个值
abc_array1[linux]=one
abc_array1[java]=two
一次赋多个值
abc_array2=([name1]=harry [name2]=jack [name3]=natasha)