shell

本文详细介绍了Shell脚本的变量定义,包括通配符、引号、脚本基本写法、变量定义规则和方式。接着讲解了简单四则运算和条件判断语法结构,如文件类型、权限、字符串等的判断。文章还涵盖了流程控制语句(if、for、while循环)的应用案例,展示了如何在Shell中进行批量操作。此外,还涉及了随机数的生成以及嵌套循环的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、变量的定义

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
上一次登录:二 927 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 923 16:50 first.sh
-rw-r--r--. 1 root root   9 926 10:55 ip.txt
-rw-r--r--. 1 root root 179 927 14:22 variable.sh
[root@Nchu150412 a]# chmod +x variable.sh
[root@Nchu150412 a]# ll
总用量 12
-rwxr-xr-x. 1 root root 207 923 16:50 first.sh
-rw-r--r--. 1 root root   9 926 10:55 ip.txt
-rwxr-xr-x. 1 root root 179 927 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、条件判断语法格式

格式1test 条件表达式
格式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 928 09:33 dir1
-rw-r--r--. 1 root root    0 928 09:32 file1
-rw-r--r--. 1 root root    6 928 09:32 file2
-rwxr-xr-x. 1 root root  207 923 16:50 first.sh
-rw-r--r--. 1 root root    9 926 10:55 ip.txt
-rwxr-xr-x. 1 root root  179 927 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 928 09:33 dir1
-rw-r--r--. 1 root root    0 928 09:32 file1
-rw-r--r--. 1 root root    6 928 09:32 file2
-rwxr-xr-x. 1 root root  207 923 16:50 first.sh
-rw-r--r--. 1 root root    9 926 10:55 ip.txt
lrwxrwxrwx. 1 root root    5 928 09:38 test1 -> file1
-rwxr-xr-x. 1 root root  179 927 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 928 09:33 dir1
-rw-r--r--. 1 root root    6 928 09:32 file2
-rwxr-xr-x. 1 root root  207 923 16:50 first.sh
-rw-r--r--. 1 root root    9 926 10:55 ip.txt
lrwxrwxrwx. 1 root root    5 928 09:38 test1 -> file1
-rwxr-xr-x. 1 root root  179 927 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 928 09:33 dir1
-rw-r--r--. 1 root root    6 928 09:32 file2
-rwxr-xr-x. 1 root root  207 923 16:50 first.sh
-rw-r--r--. 1 root root    9 926 10:55 ip.txt
lrwxrwxrwx. 1 root root    5 928 09:38 test1 -> file1
-rwxr-xr-x. 1 root root  179 927 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 928 09:33 dir1
-rw-r--r--. 1 root root    0 928 10:36 file1
-rw-r--r--. 1 root root    6 928 09:32 file2
-rwxr-xr-x. 1 root root  207 923 16:50 first.sh
-rw-r--r--. 1 root root    9 926 10:55 ip.txt
lrwxrwxrwx. 1 root root    5 928 09:38 test1 -> file1
-rwxr-xr-x. 1 root root  179 927 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
上一次登录:三 928 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 - 
密码:
上一次登录:三 928 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 928 09:33 dir1
-rw-r--r--. 1 root root    6 928 10:40 file1
-rw-r--r--. 1 root root    6 928 09:32 file2
-rwxr-xr-x. 1 root root  207 923 16:50 first.sh
-rw-r--r--. 1 root root    9 926 10:55 ip.txt
lrwxrwxrwx. 1 root root    5 928 09:38 test1 -> file1
-rwxr-xr-x. 1 root root  179 927 14:22 variable.sh
[root@Nchu150412 a]# rm -f file1
[root@Nchu150412 a]# ll
总用量 20
drwxr-xr-x. 2 root root 4096 928 09:33 dir1
-rw-r--r--. 1 root root    6 928 09:32 file2
-rwxr-xr-x. 1 root root  207 923 16:50 first.sh
-rw-r--r--. 1 root root    9 926 10:55 ip.txt
lrwxrwxrwx. 1 root root    5 928 09:38 test1 -> file1
-rwxr-xr-x. 1 root root  179 927 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 928 09:33 dir1
-rw-r--r--. 1 root root    0 928 16:54 file1
-rw-r--r--. 1 root root    6 928 09:32 file2
-rwxr-xr-x. 1 root root  207 923 16:50 first.sh
-rw-r--r--. 1 root root    9 926 10:55 ip.txt
lrwxrwxrwx. 1 root root    5 928 09:38 test1 -> file1
-rwxr-xr-x. 1 root root  179 927 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的朋友对输入密码都不会陌生,比如使用超级用户执行命令,又比如scpssh连接远程主机等等。如果我们脚本里面有scp的操作,总不可能执行一次scp就输入密码一次,这样就需要一个人盯着脚本运行了。 为了解决这个问题,我们需要一个自动输入密码的功能。

expect是建立在tcl基础上的一个工具,它可以让一些需要交互的任务自动化地完成。相当于模拟了用户和命令行的交互操作,实现自动应答。

一个具体的场景:远程登陆服务器,并执行命令。

(二)expect是什么?

expect是一个免费的编程工具,用来实现自动的交互式任务,而无需人为干预。说白了,expect就是一套用来实现自动交互功能的软件。

在实际工作中,我们运行命令、脚本或程序时,这些命令、脚本或程序都需要从终端输入某些继续运行的指令,而这些输入都需要人为的手工进行。而利用expect,则可以根据程序的提示,模拟标准输入提供给程序,从而实现自动化交互执行。这就是expect!!!

(三)expect基础

在使用expect时,基本上都是和以下四个命令打交道:

命令作用
send用于向进程发送字符串
expect从进程接收字符串
spawn启动新的进程
interact允许用户交互

send命令接收一个字符串参数,并将该参数发送到进程。
expect命令和send命令相反,expect通常用来等待一个进程的反馈,我们根据进程的反馈,再发送对应的交互命令。
spawn命令用来启动新的进程,spawn后的sendexpect命令都是和使用spawn打开进程进行交互。
interact命令用的其实不是很多,一般情况下使用spawnsendexpect命令就可以很好的完成我们的任务;但在一些特殊场合下还是需要使用interact命令的,interact命令主要用于退出自动化,进入人工交互。比如我们使用spawnsendexpect命令完成了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)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值