shell编程初始shellshell 定义shell的分类和切换使用场景shell 特性回顾bash 初始化shell 脚本规范变量的类型练习1练习二脚本运行创建bash脚本(shell脚本)bash脚本执行变量置换命令替换变量替换变量替换-匹配截取basename & dirname千锋云计算学院shell编程-流程控制shell编程-条件结构shell脚本之流程控制shell分支if语句shell 分支case语句shell编程-循环结构shell循环-for语句shell 循环while语句shell循环until语句shell 循环控制shift、continue、break、exit实战-shell版本jumpserver开发(堡垒机) 千锋云计算学院shell 编程-函数shell 函数function函数调用之二shell 编程-数组数组定义访问数组千 锋 云 计 算 学 院正则表达式RE No.1 正则表达式基本元字符No.2正则表达式拓展元字符正则判断shell 编程-grepgrep使用grep正则过滤千锋云计算学院shell 编程-SEDsed基本用法打印搜索替换多重编辑选项地址(定址)sed流编辑器命令用法及解析sed常见操作千 锋 云 计 算 学 院shell编程之awk一、awk简介二、awk的形式语法格式三、awk工作原理示例四、记录与字段相关内部变量:实战关系运算符号示例常见使用真实案例经典案例千 锋 云 计 算 学 院shell 编程-ExpectNo.1 expect的安装 --ansibleNo.2 expect的语法-------通过expect可以实现将交互式的命令变为非交互式执行,不需要人为干预(手动输入)实战非交互式ssh连接:作业shell 编程-运维脚本实战千 锋 云 计 算 学 院
xxxxxxxxxx
程序 语言 编程
----------------------------------
语言
自然语言:汉语、英语
计算机语言:c语言、c++、(java php python go shell)
编译型语言 c c++ java
解释型语言 php python bash
编译型语言:编译型语言的首先将源代码编译生成机器语言,再由机器运行机器码(二进制)。像C/C++等都是编译型语言。
解释型语言:源代码不是直接翻译成机器语言,而是先翻译成中间代码,再由解释器对中间代码进行解释运行。比如Python/JavaScript/Shell等都是解释型语言
c 编译型执行代码需要编译成cpu能认识的二进制码 x86指令集
java 编译型执行编译-->字节码,cpu不能直接运行,只能被Java虚拟机执行
shell 解释型语言执行 慢
xxxxxxxxxx
Shell 也是一种程序设计语言,它有变量,关键字,各种控制语句,有自己的语法结构,利用shell程序设计语 言可以编写功能很强、代码简短的程序。
shell是外壳的意思,就是系统的外壳,我们可以通过shell的命令来控制和操作操作系统,比如linux中的shell命令就包括ls、cd、pwd等等,总结来说shell就是一个命令解释器,他通过接收用户输入的shell命令来启动、停止程序的运行或者对计算机进行控制。
xxxxxxxxxx
[root@linux-server ~]# cat /etc/shells
/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
#默认shell: bash shell
#centos中脚本使用的默认shell 为/usr/bin/sh
查看当前正在使用的shell
[root@linux-server ~]# echo $SHELL
/bin/bash
shell的修改
# vim /etc/passwd 编辑登录shell
xxxxxxxxxx
Shell 能做什么?
1. 自动化批量系统初始化程序 (update,软件安装,时区设置,安全策略...)#初始化脚本
2. 自动化批量软件部署程序 (LAMP,LNMP,Tomcat,LVS,Nginx)#一键安装la\nmp环境,通过脚本自动上线代码
3. 应用管理程序 (KVM)#通过脚本批量创建虚拟机
4. 日志分析处理程序(PV, UV, 200, !200,grep/awk)
5. 自动化备份恢复程序(MySQL完全备份/增量 + Crond)
6. 自动化信息采集及监控程序(收集系统/应用状态信息,CPU,Mem,Disk,Net,TCP Status,Apache,MySQL)
7. 配合Zabbix信息采集(收集系统/应用状态信息,CPU,Mem,Disk,Net,Apache,MySQL)
8. 9*9乘法表、俄罗斯方块,打印三角形,打印圣诞树,打印五角星,运行小火车,坦克大战,排序实现
9. Shell可以做任何运维的事情(一切取决于业务需求)
shell常见元素
xxxxxxxxxx
文件描述符与输出重定向:
在 shell程序中,最常使用的FD (file descriptor) 大概有三个, 分别是:
0: Standard Input (STDIN)
1: Standard Output (STDOUT)
2: Standard Error Output (STDERR)
在标准情况下, 这些FD分别跟如下设备关联:
stdin(0): keyboard 键盘输入,并返回在前端
stdout(1): monitor 正确返回值 输出到前端
stderr(2): monitor 错误返回值 输出到前端
>a.txt
1>a.txt
2>a.txt
&>a.txt
1>&2
2>&1
一般来说, "1>" 通常可以省略成 ">".
1>&2 正确返回值传递给2输出通道 &2表示2输出通道,之前如果有定义标准错误重定向到某log文件,那么标准输出也重定向到这个log文件,如果此处错写成 1>2, 就表示把1输出重定向到文件2中.
2>&1 错误返回值传递给1输出通道, 同样&1表示1输出通道.
例子. 当前目录下只有a.txt,没有b.txt
[root@linux-server ~]# touch a.txt
[root@linux-server ~]# ls a.txt b.txt 1>file.out 2>&1
[root@linux-server ~]# cat file.out
ls: cannot access b.txt: No such file or directory
a.txt
现在, 正确的输出和错误的输出都定向到了file.out这个文件中, 而不显示在前端 =================================
[root@linux-server ~]# cat >> b.txt <<!
> ni hao a haha
> !
[root@linux-server ~]# cat b.txt
ni hao a haha
注:这里也可以使用EOF需要成对使用即可!
xxxxxxxxxx
用户登录时相关的bash配置文件 (登录脚本)属于全局配置文件:
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
个人配置文件,每个用户的配置文件
~/.bsah_profile
~/.bashrc
#profile类的文件: 设定环境变量,运行命令或脚本,用户在登录的时候会自动生效
#bashrc类的文件: 定义命令别名
用户登录时加载bash配置文件的过程:
登录式shell加载配置文件过程
~/.bash_profile --> ~/.bashrc --> /etc/bashrc ---> /etc/profile --> /etc/profile.d/*.sh
#这里的优先级是影响范围最小的优先级最高
非登录式shell加载配置文件过程
~/.bashrc --> /etc/bashrc --> /etc/profile.d/*.sh
下面的文件为系统的每个用户设置环境信息Shell设置文件:
/etc/profile(系统级)启动时执行
这是系统最主要的shell设置文件,也是用户登陆时系统最先检查的文件,有关重要的环境变量都定义在此,其中包括 PATH,USER,MAIL,HOSTNAME,HISTSIZE,INPUTRC等。而在文件的最后,它会检查并执行/etc/profile.d/*.sh的脚本。
~/.bash_login(用户级)登录时执行
如果~.bash_profile文件不存在,则系统会转而读取.bash_login这个文件内容。这是用户的登陆文件,在每次用户登陆系统时,bash都会读此内容,所以通常都会将登陆后必须执行的命令放在这个文件中。
~/.bash_logout 离开时执行如果想在注销shell前执行一些工作,都可以在此文件中设置。 例如:
# vi ~.bash_logout
clear
仅执行一个clear命令在你注销的时候
~/.bash_history(用户级) #这个文件会记录用户先前使用的历史命令。
bash shell 特性
xxxxxxxxxx
补全 tab键 #yum -y install bash-completion
历史 ---history
别名 --alias
快捷键 --ctrl+c \ ctrl+z\ ctrl+l
前后台作业 ---jobs---bg ---fg
重定向 ---> >> 2> 2>> < &
管道 -----|
#命令排序执行: ; && ||
&&:逻辑与,前面执行成功,后面才执行。前面命令执行失败,后面命令也不执行
||:逻辑或,前面执行失败,后面执行,前面命令执行成功,后面不执行。
;:从左往右按顺序执行,不管前面执行成功与否,后面都执行
通配符:[] {} ? *
正则表达式 脚本
历史命令
xxxxxxxxxx
查看历史命令
[root@linux-server ~]# history
调用历史命令
上下健
!关键字
!历史命令行号
!! 执行上一条命令
!$ 上一条命令的最后一个参数
esc . 上一条命令的最后一个参数
Ctrl+r 在历史命令中查找,输入关键字调出之前的命令
别名
查看别名
[root@linux-server ~]# alias
设置别名
临时设置
[root@linux-server ~]# aa=88
[root@linux-server ~]# echo $aa
88
永久设置
# vim /root/.bashrc
# source /root/.bashrc #让文件生效
小小技巧:显示历史命令执行时间
1.设置变量:
[root@linux-server ~]# vim /etc/profile #在最后添加
HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S"
[root@linux-server ~]# source /etc/profile #让设置的环境变量生效
2.再次执行history查看结果
Bash 部分快捷键
xxxxxxxxxx
Ctrl+a 切换到命令行开始(跟home一样,但是home在某些unix环境下无法使用)
Ctrl+u 清除剪切光标之前的内容
Ctrl+k 清除剪切光标之后的内容
ctrl+y 粘贴刚才所删除的字符
Ctrl+r 在历史命令中查找,输入关键字调出之前的命令
Ctrl+l 清屏
Ctrl+c 终止
Ctrl+e 切换到命令行末尾
通配符置换
xxxxxxxxxx
在 Shell命令中,通常会使用通配符表达式来匹配一些文件
*,?,[],{}
例:
字符 含义 实例
* 匹配 0 或多个字符 a*b a与b之间可以有任意长度的任意字符, 也可以一个也没有, 如aabcb, axyzb, a012b, ab。
? 匹配任意一个字符 a?b a与b之间必须也只能有一个字符, 可以是任意字符, 如aab, abb, acb, a0b 。
[list] 匹配 list 中的任意单一字符 a[xyz]b a与b之间必须也只能有一个字符, 但只能是 x 或 y 或 z, 如: axb, ayb, azb 。
[!list] 匹配 除list 中的任意单一字符 a[!0-9]b a与b之间必须也只能有一个字符, 但不能是阿拉伯数字, 如axb, aab, a-b。
[c1-c2] 匹配 c1-c2 中的任意单一字符 如:[0-9] [a-z] a[0-9]b 0与9之间必须也只能有一个字符 如a0b, a1b... a9b。
{string1,string2,...} 匹配 sring1 或 string2 (或更多)其一字符串 a{abc,xyz,123}b a与b之间只 能是abc或xyz或123这三个字符串之一。
xxxxxxxxxx
[root@linux-server tmp]# rm -rf ./*
[root@linux-server tmp]# touch aabcb axyzb a012b ab acb
[root@linux-server tmp]# ls
a012b aabcb ab acb axyzb
[root@linux-server tmp]# ls a*b
a012b aabcb ab acb axyzb
[root@linux-server tmp]# ls a?b
acb
[root@linux-server tmp]# rm -rf ./*
[root@linux-server tmp]# touch axb ayb azb axyb
[root@linux-server tmp]# ls
axb axyb ayb azb
[root@linux-server tmp]# ls a[xy]b
axb ayb
[root@linux-server tmp]# ls a[!xy]b
azb
[root@linux-server tmp]# ls a[!x]b
ayb azb
[root@linux-server tmp]# rm -rf ./*
[root@linux-server tmp]# touch a0b a1b a9b
[root@linux-server tmp]# ls a[0-9]b
a0b a1b a9b
[root@linux-server tmp]# rm -rf ./*
[root@linux-server tmp]# touch aabcb axyzb a012b ab
[root@linux-server tmp]# ls a{abc}b
ls: cannot access a{abc}b: No such file or directory
[root@linux-server tmp]# ls a{abc,xyz}b
aabcb axyzb
[root@linux-server tmp]# ls a{abc,xyz,012}b
a012b aabcb axyzb
xxxxxxxxxx
[root@linux-server ~]# vim helloworld.sh ---.sh代表这个文件是个shell脚本,
拓展名后缀,如果省略.sh则不易判断该文件是否为shell脚本
1. #!/usr/bin/env bash ---shebang蛇棒, 解释器, 翻译 2. #
2. #
3. # Author: soso666
4. # Email: soso666@163.com ---这就是注释, 你没看错
5. # Github: https:github.com/soso666
6. # Date: 2019/12/24
7. printf "hello world\n"
功能说明:打印hello world
[root@linux-server ~]# sh helloworld.sh
hello world
[root@linux-server ~]# chmod +x helloworld.sh
[root@linux-server ~]# ./helloworld.sh
[root@linux-server ~]# /root/helloworld.sh
hello world
第一行: “#!/usr/bin/env bash”叫做shebang, shell语法规定shell脚本文件第一行为整个文件的解释器
第二行: 为“#”开头的行为注释行默认不会被程序所读取, 用来说明文件及标定所属人员使用, 也可用来解释程序
第七行: 为格式化打印语句printf, printf可以把后面的“hello world”打印到指定的终端中, \n 为换行符
bash 脚本测试:
1.这将执行该脚本并显示所有变量的值
[root@linux-server ~]# sh -x /root/helloworld.sh
+ printf 'hello world\n'
hello world
2.不执行脚本只是检查语法模式,将返回所有错误语法
[root@linux-server ~]# sh -n /root/helloworld.sh
3.执行脚本前把脚本内容显示在屏幕上
[root@linux-server ~]# sh -v /root/helloworld.sh
#
# Author: soso666
# Email: soso666@163.com
# Github: https:github.com/soso666
# Date: 2019/12/24
printf "hello world\n"
hello world
xxxxxxxxxx
变量:bash作为程序设计语言和其它高级语言一样也提供使用和定义变量的功能,简单说就是让一个特定的字符串代表不固定的内容 a=123,echo $a
#分为:预定义变量、环境变量、自定义变量、位置变量
xxxxxxxxxx
预定义变量:预定义的特殊变量有着特殊的含义,用户不可以更改,所有的预定义变量都由$符号和另外一个符号组成,常用的预定义特殊变量如下:
$$ 当前进程PID
$? 命令执行后的返回状态.0 为执行正确,非 0 为执行错误
$# 位置参数的数量
$* 所有位置参数的内容
$@ 显示所有的参数
$! 上一个后台进程的PID (wait命令中使用,后面讲)
拓展:$* 和 $@ 有什么区别
[root@linux-server ~]# echo $? 最后一次执行的命令的返回状态。如果这个变量的值为 0,则证明上一条命令正确执行;如果这个变量的值为非 0 ,则 证明上一条命令执行错误
[root@linux-server ~]# echo $$ 当前进程的进程号(PID)
[root@linux-server ~]# echo $! 后台运行的最后一个进程的进程号(PID)
[root@linux-server ~]# ls
anaconda-ks.cfg a.txt b.txt file.out helloworld.sh #ls命令正确执行
[root@linux-server ~]# echo $?
0
#预定义变量"$?"的值是0,证明上一条命令正确执
#输出当前进程的PID
[root@linux-server ~]# vim variable.sh
echo "The current process is $$"
[root@linux-server ~]# sh variable.sh
The current process is 1416
#这个PID就是variable.sh脚本执行时生成的进程的PID
[root@linux-server ~]# sleep 3000 &
[1] 1418
#符号"&"的意思是把命令放入后台执行
[root@linux-server ~]# echo $!
1418
xxxxxxxxxx
自定义变量:就是自己设置的变量只能在当前终端和脚本中使用
变量名称=值 #定义变量
#变量名称:只能由字母,数字,下划线组成,不能以数字开头;
#注意:应该让变量名称有意义;
= 赋值符号 前后不能有空格 ;
值: 所有的字符串和数字都可以;
引用变量: $变量名 或 ${变量名}。
示例:
[root@linux-server ~]# a=100
[root@linux-server ~]# echo $a
100
[root@linux-server ~]# echo $aa # 这里输出为空,因为解释器认为$aa是变量
[root@linux-server ~]# echo ${a}a
100a
查看变量: echo $变量名
取消变量: unset 变量名,仅在当前shell中有效
xxxxxxxxxx
环境变量:shell在开始执行时已经定义好的,就是系统执行环境的一些设置
# env #env是 environment (环境) 的简写,所有的环境变量(包含自定义的环境变量)
# set #列出系统中所有的变量,包括自定义的变量
export 变量名 #使自定义的变量成为环境变量,才使用这个参数。环境变量拥有可继承性:export之后就拥有继承性环境变量可以被向下继承
临时生效
[root@linux-server ~]# IPADDR=192.168.1.1
[root@linux-server ~]# echo $IPADDR
192.168.1.1
永久生效
写到4个登陆脚本中 ~/.bashrc ~/profile 更好放在/etc/profile.d/目录下建立独立的环境变量配置文件
[root@linux-server ~]# vim /etc/profile.d/test.sh
IPADDT=192.168.1.1
[root@linux-server ~]# source /etc/profile.d/test.sh #让环境变量生效
[root@linux-server ~]# echo $IPADDT
192.168.1.1
常用环境变量:USER UID HOME HOSTNAME PWD PS1 PATH
PATH:存储所有命令所在的路径
xxxxxxxxxx
#子进程 仅会继承父 shell 的环境变量, 不会继承父 shell 的自定义变量
[root@localhost ~]# bash # 打开一个子 shell
[root@localhost ~]# export a=hello # 在 子 shell 声明一个环境变量
[root@localhost ~]# bash # 在子 shell 中再打开一个 子 shell
[root@localhost ~]# echo $a # 变量可以生效
hello
[root@localhost ~]# exit # 退出 子 shell 的 子 shell
exit
[root@localhost ~]# exit # 退出 子 shell
exit
[root@localhost ~]# echo $a # 在 当前 shell 中, 其子 shell 声明的环境变量是无效的
[root@localhost ~]#
xxxxxxxxxx
编写一个shell脚本,用于搜集其执行主机的信息,打印结果如下:
[root@linux-server ~]# mkdir /opt/test/script
[root@linux-server ~]# cd /opt/test/script
[root@linux-server script]# vim test.sh
[root@linux-server script]# chmod +x test.sh
[root@linux-server script]# ./test.sh
现在的时间是: 2020-08-22-17:34:03
当前的用户是: root
当前的用户标识: 0
当前的主机名称是: linux-server
当前可用网卡IP是: 192.168.246.148/24
##脚本源码如下
# 获取主机基本信息
time=`date +%F-%T`
ip=`ip a | grep ens33 | awk 'NR==2 {print $2}'`
echo "现在的时间是:" $time
echo "当前的用户是:" $USER
echo "当前的用户标识:" $UID
echo "当前的主机名称是:" $HOSTNAME
echo "当前可用网卡IP是:" $ip
取当前系统分区剩余空间:
[root@linux-server script]# df -Th | awk 'NR==6 {print $5}'
489M
取当前系统剩余内存:
[root@linux-server script]# echo "现在的剩余内存是:"`free -m |awk 'NR==2{print $4}'`
现在的剩余内存是:16G
取当前cpu平均负载:
[root@linux-server script]# echo 现在cpu的`uptime | cut -d, -f4-` #-d指定分隔符,-f指定显示区域,3-第三列以后(包括第三列)
现在cpu的 load average: 0.00, 0.01, 0.05
方式二:
[root@linux-server script]# echo 现在cpu的`uptime | awk -F "," '{print $3,$4,$5}'`
现在cpu的 load average: 0.00 0.01 0.05
xxxxxxxxxx
编写一个脚本实现收集主机的基本信息,最后脚本还会将这些信息写入一个日志文件.
[root@linux-server script]# vim xinxi.sh
#获取主机基本信息
centime=`date '+%Y-%m-%d %H:%M:%S'`
nowtime=`uptime |awk '{print $1}'`
username=$USER
verage=`uptime |awk -F',' '{print $3,$4,$5}'`
myname=xuange
cat >>file1.txt <<EOF
echo "时间:$centime"
echo "系统的当前时间是: $nowtime"
echo "系统当前负载: $verage"
echo "当前的用户是: $username"
echo "我的名字是: $myname"
EOF
[root@linux-server script]# chmod +x xinxi.sh
[root@linux-server script]# ./xinxi.sh
[root@linux-server script]# cat file1.txt
===========================================================
xxxxxxxxxx
位置变量也叫位置参数:在脚本代码中调用通过命令行传递给脚本的参数
$1 $2 $3 $... #分别对应传递给脚本内容里面的第1、第2等参数
例子:
# /test.sh start #start是第1个位置参数
#/test.sh 2 3 5 hello #2是第1个位置参数,3是第2个位置参数...依次类推
例子:
[root@linux-server ~]# cd /opt/test/script/
[root@linux-server script]# vim weizhi.sh
echo 我的第一个位置参数是:$1
echo 我的第二个位置参数是:$2
echo 我的第三个位置参数是:$3
echo 我的第四个位置参数是:$4
echo 一共有 $# 个位置参数
echo 你输入的参数分别是:$*
[root@linux-server script]# chmod +x weizhi.sh
[root@linux-server script]# ./weizhi.sh 1 3 4 6
我的第一个位置参数是:1
我的第二个位置参数是:3
我的第三个位置参数是:4
我的第四个位置参数是:6
一共有 4 个位置参数
你输入的参数分别是:1 3 4 6
xxxxxxxxxx
变量运算
算式运算符: +、-、*、/、()、%取余(取模)
如:(5+3)*2
运算方式:$(()) $[] expr
$(()):例子
# echo $(( 5+2-(3*2)/5 ))
6
# echo $(((3*2)/5))
1
$[]:例子
# echo $[ 5 + 2 - (3*2)/5 ]
6
expr:例子
# expr 5 + 3
8
#注意:运算符号两边的空格必须写
乘法运算:
[root@linux-server script]# expr 5 \* 3
15
[root@linux-server script]# expr 5 '*' 3
15
脚本案例:
[root@localhost ~]# vim test1.sh
a=2
b=13
echo "$a和$b的和是: $(( $a + $b ))"
echo "$a乘$b的值是: $(( $a * $b ))"
echo "$a和$b的差是: $(( $a - $b ))"
echo "$a和$b的商是: $(( $b / $a ))"
echo "$a和$b的余是: $(( $b % $a ))"
#脚本中常用的方式
echo "$a和$b的和是: `expr $a '+' $b`"
=================================================================
取1到6之间的随机数:
# echo $RANDOM
# echo $(($RANDOM % 6 + 1))
5
取1-10之间的随机数:
# echo $(($RANDOM % 10 + 1))
5
[root@linux-server script]# vim sjs.sh
echo $(($RANDOM % 50 + 1 ))
#这串代码实现了随机生成从1~50之间是数
这串代码特别简单,就是利用RANDOM这个随机数生成器进行取余就能够实现,至于为什么取余时需要+1是因为在取余时如果被整除那么余数会是0,这样就不在限定范围内了
xxxxxxxxxx
浮点运算
bash本身不能做小数计算:需要bc命令转换
# yum install -y bc
# echo "2.6*4" | bc
# echo "2^4" | bc
xxxxxxxxxx
变量引用
#转义:\
1.当一个字符被引用时,其特殊含义被禁止,把有意义的变的没意义,把没意义的变的有意义
转义案例:
[root@linux-server script]# echo you now $1250
you now 250
[root@linux-server script]# echo you now \$1250
you now $1250
完全引用:'' #强引 硬引 #指的是被引号包围起来的变量名不会进行不会进行解析,原样变量名原样输出,这种方式比较适合定义显示纯字符串的情况,不希望解析变量、命令等的场景。
部分引用:"" #弱引 软引 #指的是被引号包围起来的变量名会先进行解析,然后将变量的解析结果输出来。这种方式适合字符串中附带有变量和命令并且想将其解析后再输出的变量定义。
例子:
[root@linux-server script]# num=1
[root@linux-server script]# echo 1901班有$num个女生
1901班有1个女生
[root@linux-server script]# echo "1901班有$num个女生"
1901班有1个女生
[root@linux-server script]# echo '1901班有$num个女生'
1901班有$num个女生
脚本例子
# vim a.sh
echo 'echo $USER' >> b.sh
======================================================
#读取用户标准输入:read
read:功能就是读取键盘输入的值,并赋给变量
#read -t 5 var
#read -p "提示信息" var
read后面的变量var可以只有一个,也可以有多个,这时如果输入多个数据,则第一个数据给第一个变量,第二个数据给第二 个变量,如果输入数据个数过多,则最后所有的值都给最后一个变量
read -p "请输入你的用户名和密码还有你的年龄: " name pass age
echo "你的名字是 $name"
echo "你的密码是 $pass"
echo "你的年龄是 $age"
案例1:
[root@linux-server script]# vim readtest.sh
# read test
read -p "请输入你的银行卡帐号" num
read -p "请在五秒内输入密码" -t 5 pass
echo "你的密码错误!"
[root@linux-server script]# ./readtest.sh
案例2:
#自定义程序结果的正确或错误
[root@linux-server script]# vim readtest2.sh
read -p "Do you want to continue [Y/N]? " w
case $w in
Y|y)
echo "fine ,continue";;
N|n)
echo "ok,good bye";;
*)
echo "error choice";;
esac
exit 0
[root@linux-server script]# chmod +x readtest2.sh
[root@linux-server script]# ./readtest2.sh
案例3: #-s 选项 能够使read命令中输入的数据不显示在监视器上
[root@linux-server script]# vim readtest3.sh
read -s -p "Enter your password: " pass
echo "your password is $pass"
exit 1
[root@linux-server script]# chmod +x readtest3.sh
[root@linux-server script]# ./readtest3.sh
========================================================
取消屏幕回显
[root@linux-server script]# stty -echo #回车测试
[root@linux-server script]# stty echo #恢复回显
显示变量长度
[root@linux-server script]# a=123
[root@linux-server script]# echo ${#a} #表示$var的长度
3
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
练习:
[root@linux-server script]# cat d.sh
echo 1.配置yum客户端
echo 2.添加A记录
echo 3.一键安装lamp环境
echo 4.一键配置静态IP
read -p "请选择你想使用的功能(1/2/3/4):" num
con_ip(){
echo 这是配置IP地址的小工具
}
case $num in
1):
;;
2):;;
3):
;;
4)con_ip
;;
*)
echo "你输入的不正确!请按提示输入"
;;
esac
[root@linux-server script]# chmod +x d.sh
[root@linux-server script]# ./d.sh
xxxxxxxxxx
1.创建脚本文件
指定命令解释器
注释
编写bash指令集合
2.修改权限
xxxxxxxxxx
# chmod +x script
# ./scripts
# /shelldoc/scripts
# source ./scripts 使用当前shell执行 比如cd /tmp会改变当前shell环境,但是其他的方式不会
# bash scripts
xxxxxxxxxx
取命令结果用。把命令的结果拿出来
[root@linux-server ~]# a=`date +%m%d`
[root@linux-server ~]# echo $a
1225
[root@linux-server ~]# a=$(date +%m-%d)
[root@linux-server ~]# echo $a
12-25
#反引号亦可用$() 代替
xxxxxxxxxx
一 ${parameter:-word}
若 parameter 为空或未设置,则用 word 代替 parameter 进行替换,parameter 的值不变,若 parameter 不为空,则不替换,parameter 的值不变
# unset b
# echo ${b:-3}
-3
# echo $b
#
二 ${parameter:=word}
若 parameter 为空或未设置,则用 word 代替 parameter 进行替换,parameter 的值改变,若 parameter设置了,则不替换,parameter的值不变
# unset b
# echo ${b:=3}
3
# echo $b
3
#
三 ${parameter:+word}
若 parameter 设置了值,则用 word 代替 parameter 进行替换,parameter 的值不变
# unset b
# echo ${b:+3}
3
# echo $b
2
脚本案例
[root@localhost ~]# vim mysql.sh
read -p "请选择你要安装的mysql版本,默认为5.7 " var
echo "默认的mysql版本为${var:-5.7}"
echo "你选择的版本是$var"
x
语法:
${变量#关键词} 若变量内容从头开始的数据符合『关键词』,则将符合的最短数据切除
${变量##关键词} 若变量内容从头开始的数据符合『关键词』,则将符合的最长数据切除
${变量%关键词} 若变量内容从尾向前的数据符合『关键词』,则将符合的最短数据切除
${变量%%关键词} 若变量内容从尾向前的数据符合『关键词』,则将符合的最长数据切除
${变量/旧字符串/新字符串} 若变量内容符合『旧字符串』则『第一个旧字符串会被新字符串替代』
${变量//旧字符串/新字符串} 若变量内容符合『旧字符串』则『全部的旧字符串会被新字符串替代』
索引及切片
[root@linux-server ~]# a=12345678
[root@linux-server ~]# echo ${a:5} #从从左往右第5位开始截取,留下后三位
678
[root@linux-server ~]# echo ${a:3:4} #从第3位开始截取,留下后四位的,剩下的都不要。
4567
[root@linux-server ~]# echo ${a:2:-1} #从左往右第2位开始截取,从右往左截取第一位
34567
[root@linux-server ~]# echo ${a:2:-2}
3456
脚本案例:
[root@localhost ~]# vim test11.sh
read -s -p "请输入您的手机号 " phone
echo
echo "你的手机号是 $phone"
echo "手机号后四位是 ${phone:7}"
[root@linux-server ~]# url=www.sina.com.cn
[root@linux-server ~]# echo ${#url} #获取变量的长度
15
[root@linux-server ~]# echo ${url} #正常显示变量
www.sina.com.cn
变量内容的删除
[root@newrain ~]# echo ${url#*.} 从前往后匹配到“.”最短匹配
sina.com.cn
[root@newrain ~]# echo ${url##*.} 从前往后匹配到“.”,最长匹配
cn
[root@newrain ~]# echo ${url%.*} 从后往前匹配到“.”,最短匹配
www.sina.com
[root@newrain ~]# echo ${url%%.*} 从后往前匹配到“.”,最长匹配
www
[root@newrain ~]# echo ${url#a.} #不加*
www.sina.com.cn
[root@newrain ~]# echo ${url#*a.} #加*
com.cn
[root@localhost ~]# vim mail.sh
脚本案例:
read -p "请输入你的邮箱 " mail
echo "你的邮箱是$mail"
echo "你的邮箱服务器是${mail#*@}"
mail_host=${mail#*@}
case $mail_host in
163.com)
echo "网易服务器"
;;
126.com)
echo "126服务器"
;;
qq.com)
echo "qq邮箱"
;;
*)
echo "您输入的邮箱不正确"
exit 2
esac
参数解释:
*:表示全部字符。
%:最短尾匹配;
%%:最大尾匹配
%:从右往左
#:从左往右
用冒号截取:echo $a: : :
从哪里截取留那里。
变量内容的替换
[root@linux-server ~]# a=123456123789
[root@linux-server ~]# echo ${a/1/} #第一次匹配的被替换
23456123789
[root@linux-server ~]# echo ${a/1/0} #第一次匹配到1替换成0
023456123789
[root@linux-server ~]# echo ${a//1/} #全局的匹配被替换
2345623789
[root@linux-server ~]# echo ${a//1/x} #全局匹配到1替换成x
x23456x23789
例:
file=/dir1/dir2/dir3/my.file.txt
${file#*/}: 拿掉第一条 / 及其左边的字符串:dir1/dir2/dir3/my.file.txt
${file##*/}: 拿掉最后一条 / 及其左边的字符串:my.file.txt
${file#*.}: 拿掉第一个 . 及其左边的字符串:file.txt
${file##*.}: 拿掉最后一个 . 及其左边的字符串:txt
${file%/*}: 拿掉最后条 / 及其右边的字符串:/dir1/dir2/dir3
${file%%/*}: 拿掉第一条 / 及其右边的字符串:(空值)
${file%.*}: 拿掉最后一个 . 及其右边的字符串:/dir1/dir2/dir3/my.file
${file%%.*}: 拿掉第一个 . 及其右边的字符串:/dir1/dir2/dir3/my
记忆的方法为:
# 是去掉左边(在键盘上 # 在 $ 之左边)
% 是去掉右边(在键盘上 % 在 $ 之右边)
单一符号是最小匹配;两个符号是最大匹配(贪婪匹配)。
xxxxxxxxxx
basename 命令
basename 是去除目录后剩下的名字,取文件名
例:
[root@linux-server ~]# temp=/home/temp/1.test
[root@linux-server ~]# base=`basename $temp`
[root@linux-server ~]# echo $base
1.test
dirname 是取目录名
例:
[root@linux-server ~]# temp=/home/temp/1.test
[root@linux-server ~]# dir=`dirname $temp`
[root@linux-server ~]# echo $dir
/home/temp
xxxxxxxxxx
测试-----test 条件
#条件为真返回 0,条件为假返回 1 #语法------[ 条件 ]
test 能够理解3中类型的表达式
1.文件测试
2.字符串比较
3.数字比较
字符串
-n STRING
the length of STRING is nonzero
-n 字符串的长度 不是零成功。
-z STRING
the length of STRING is zero
-z 字符串长度。是零成功 #对于未定义或赋予空值的变量将是为空串。
STRING1 = STRING2 (等于)
the strings are equal
STRING1 != STRING2 (不等于)
the strings are not equal
# vim string.sh
while :
do
read -p "请输入你的密码: " a
pass=123456
if [ -z $a ];then
echo "您输入的密码不能为空"
exit 1
else
if [ $a = $pass ];then
echo "登录成功"
break
else
echo "您的密码输入有误,请重新输入"
fi
fi
done
#数字
-eq(equal) 等于
-ne(not equal) 不等于
-ge(Greater than or equal to) 大于等于
-le(Less than or equal to) 小于等于
-gt(greater than) 大于
-lt(less than) 小于
#文件
test
-f #存在且是正规文件
-d #存在且是目录
-h 存在且是符号链接
-b 块设备
-c 字符设备
-e #文件存在
案例:
[root@localhost ~]# vim test.sh
file=/opt/test.txt
touch /opt/test.txt
if [ -e $file ];then
echo "$file"
else
echo "文件不存在"
fi
xxxxxxxxxx
流控制:
•在一个shell脚本中的命令执行顺序称作脚本的流。大多数脚本会根据一个或多个条件来改变它们的流。
•流控制命令:能让脚本的流根据条件而改变的命令称为条件流控制命令
•exit语句:退出程序的执行,并返回一个返回码,返回码为0正常退出,非0为非正常退出,例如:
•exit 0
条件判断语法:
if ------代码返回0表示真,非0为假
if语句语法如下:
if [ list1 ];then list1:你的测试条件,你要测试什么,对什么内容做判断
list2
elif [ list3 ];then ---------------> 接着在怎么做。(多条件判断)
list4
else ---------------> 如果前面的命令没有执行成功那就执行else下面的命令。
list5
fi
例:
[root@linux-server ~]# cd /opt/test/script/
[root@linux-server script]# vim testif.sh
read -p "请输入号码: " num
if [ $num = 1 ];then
echo "1"
elif [ $num = 2 ];then
echo "2"
else
echo "输入有误!"
fi
[root@linux-server script]# chmod +x testif.sh
例:脚本if.sh,必须在脚本后加上适当的参数脚本才能正确执行
[root@linux-server script]# vim if.sh
if [ "$1" = "hello" ]; then
echo "Hello! How are you ?"
elif [ "$1" = "" ]; then
echo "You MUST input parameters"
else
echo "The only accept parameter is hello"
fi
[root@linux-server script]# chmod +x if.sh
测试:
[root@linux-server script]# ./if.sh
[root@linux-server script]# ./if.sh hello
[root@linux-server script]# ./if.sh 434
练习:
1)检测apache是否运行,如果没有运行则启动,并记录启动的时间,保存到日志中。
2)测试ip地址主机位从2到100的机器是否存活,并把存活的机器记录到文本文件alivehost.txt内。(使用ping命令)
案例
ip=192.168.198
for i in {2..100}
do
ping -c1 $ip.$i &> /dev/null
if [ $? -eq 0 ];then
echo "$ip.$i is up" >> activehost.txt
else
echo "$ip.$i is down"
fi
done
多个条件联合
&&:逻辑与,前面执行成功,后面才执行。前面命令执行失败,后面命令也不执行
if [ $condition1 ] && [ $condition2 ];then
if [[ $condition1 && $condition2 ]];then
||:逻辑或,前面执行失败,后面执行,前面命令执行成功,后面不执行。
if [ $condition1 ] || [ $condition2 ];then
if [[ $condition1 || $condition2 ]];then
练习:
编写脚本port.sh,执行脚本后显示系统的httpd、ftp、ssh、这些服务的端口
xxxxxxxxxx
作业
1. ping主机测试
2. 判断一个用户是否存在
3. 判断当前内核主版本是否为3,且次版本是否大于10
4. 判断vsftpd软件包是否安装,如果没有则自动安装 (yum是否能用,不能用自动修复,安装完成测试以下,是否能用。)
5. 判断httpd是否运行
6. 判断指定的主机是否能ping通,必须使用$1变量
7. 报警脚本,要求如下:
根分区剩余空间小于20%
内存已用空间大于80%
向用户alice发送告警邮件
配合crond每5分钟检查一次
echo "邮件正文" | mail -s "邮件主题" alice 可以报警
8. 判断用户输入的是否是数字
read -p "请输入:" get
case $get in
[0-9][0-9]*) #判断输入是否是数字
echo -e "你输入是数字。\n"
;;
*)
echo -e "你输入的不是数字。\n"
;;
esac
xxxxxxxxxx
case 语句是 shell 中流控制的第二种方式,语法如下:
case $变量 in
pattern1)
list1
;; ---------------------结尾。
pattern2)
list2
;;
... ...
patternN)
listN
;;
*) --------------------> 如果前面命令没有执行成功那么执行下面这个
list*
;;
esac
命令;;表明流应该跳转到case语句的最后,类似C语言中的break指令。
第一行: 声明case关键字调用case语法, 紧跟的“变量”一般为用户的输入值, in代表从下方的各个模式进行匹配
第2-4行: 匹配到“pattern1”后进行命令的输出或执行, pattern1: 一般为字符或数值
第11-12行: 当用户输入的字符不存在匹配模式时, 直接执行或打印*)下的命令或语句
实例1:
[root@linux-server script]# vim foo.sh
case $1 in
foo)
echo "bar"
;;
bar)
echo "foo"
;;
*)
echo "Usage:$0 '{foo|bar}'"
;;
esac
[root@linux-server script]# chmod +x foo.sh
[root@linux-server script]# ./foo.sh bar
练习:建立脚本case.sh,当执行时,要求我们在键盘输入适当的值(one|two|three),当输入正确时并打印,当输入错误 时会提示你,应该输入正确的值。
示例2:
xxxxxxxxxx
[root@linux-server script]# vim system_tools.sh
cat <<-EOF
+-------------------------------------------------------------------------+
| System_tools V1.0 |
+-------------------------------------------------------------------------+
| a. Stop And Disabled Firewalld. |
| b. Disabled SELinux Secure System. |
| c. Install Apache Service. |
| d. Quit |
+-------------------------------------------------------------------------+
EOF
echo "Please input your select: " && read var
case "$var" in
"a")
systemctl stop firewalld && systemctl disable firewalld
;;
"b")
setenforce 0
;;
"c")
yum -y install httpd httpd-tools
;;
"d")
exit
;;
*)
printf "请按照上方提供的选项输入!!!\n"
;;
esac
[root@linux-server script]# chmod +x system_tools.sh
[root@linux-server script]# ./system_tools.sh
练习:
1.建立脚本service.sh,当执行的时候要求输入(1、2、3、4、5)时安装对应的httpd、vim、wget、更换aliyum等功能,当输入错误 时会提示你,应该输入正确的值。
xxxxxxxxxx
for i in {取值范围} #for是关键字 i是变量名 in是关键字
do #循环体的开始
循环体
done #循环体的结束
xxxxxxxxxx
实战1:
[root@linux-server script]# vim for.sh
#
# Author:
# Date: 2019/**/**
for i in {1..100}
do
echo $i
done
xxxxxxxxxx
[root@linux-server script]# vim for1.sh
for (( i=1;i <= 5;i++ ))
do
echo "$i"
done
[root@linux-server script]# chmod +x for1.sh
[root@linux-server script]# ./for1.sh
参数解释:
默认值 i=1
条件 i<=多少?取决于定义,为用户输入的变量,先条件成立在执行命令
增幅 i++ 执行一次加一
区别:
i++===先赋值在运算
++i===先运算在赋值
例子
[root@localhost script]# i=1
[root@localhost script]# h=1
[root@localhost script]# let x=i++
[root@localhost script]# echo $x
1
[root@localhost script]# echo $i
2
[root@localhost script]# let y=++h
[root@localhost script]# echo $y
2
[root@localhost script]# echo $h
2
测试成产环境的主机存活性,将up的ip保存在一个文件中,将down的ip保存在一个文件中
xxxxxxxxxx
[root@linux-server script]# vim ip.sh
# Author:
src_ip="192.168.246"
for i in {2..254}
do
{
ping -c1 $src_ip.$i &>/dev/null
if [ $? -eq 0 ];then
echo "alive: $src_ip.$i" >> ip_up.txt
echo "alive: $src_ip.$i"
else
echo "down: $src_ip.$i" >> ip_down.txt
echo "down: $src_ip.$i"
fi
} &
done
wait
echo "finish..."
[root@linux-server script]# chmod +x ip.sh
[root@linux-server script]# ./ip.sh
参数详解:
wait:等待上面命令后台执行结束后(即上一个的进程终止),在执行下面的echo命令
for循环批量创建用户
xxxxxxxxxx
[root@linux-server script]# vim user.sh
read -p "请设置用户名/数量/密码: " prefix num pass
cat <<-EOF
用户前缀:$prefix
用户数量:$num
用户密码:$pass
EOF
for i in $(seq 1 $num)
do
user=$prefix$i
id $user &> /dev/null
if [ $? -eq 0 ];then
echo "$user is already exist!"
exit 0
else
useradd $user &> /dev/null
echo $pass | passwd --stdin $user &>/dev/null
fi
done
echo "starting create users..."
[root@linux-server script]# chmod +x user.sh
[root@linux-server script]# ./user.sh
参数详解:
seq 打印序列号,只跟数字
seq 命令用于产生从某个数到另外一个数之间的所有整数。
打印奇数: # seq 1 2 10:表示1到10以内的奇数,2是步长,因为中间隔2.
seq -w $num :-w 所有数取最宽的宽度,前面没有的自动补零。
seq命令的原理就不说了,这里说说为什么不能在{ }中使用变量。其实原因写在bash的man手册中:
大意是说,Bash中会最先展开{ }中的内容,这个时候$NUM还不会被具体的值替代,所以是i在循环中读取的是‘{1..$NUM}’的一个完整的字符串,输出时$NUM会被10替代,就有了'{1..10}'这样的结果。
xxxxxxxxxx
while 条件 #while关键字,条件和if的条件一样,#while循环当条件为真的时候循环同时会一直循环,也就所说的死循环,为假时不循环
do
循环体
done
#注意:while循环处理文件里面的行比较擅长,不管有没有空格都是一行。
案例:
# vim c.sh
i=1
while [ $i -lt 50 ]
do
echo $i
done
注意观察,#请问如何能够自动终止
#在shell中,let命令用于指定算术运算,即 let expr
完善系统工具的输出及操作性
xxxxxxxxxx
#通过一个文件批量创建用户:
#背景:写一个脚本,满足以下需求及应用,如一个文件的内容如下,根据文件内容实现批量创建用户,第一列为用户名,第二列为密码
[root@localhost script]# vim user_pass.txt #创建用户和密码文件
user1 qfedu123
user2 qfedu456
user3 qfedu567
user4 qfedu789
user5 qfedu012
[root@localhost script]# vim create_user.sh #编写脚本
[ $UID -ne 0 ] && exit 1
while read line
do
user=`echo $line | awk '{print $1}'`
pass=`echo $line | awk '{print $2}'`
id $user &> /dev/null || useradd $user && echo $pass | passwd $user --stdin
done < /opt/test/script/user_pass.txt
[root@localhost script]# chmod +x create_user.sh
[root@localhost script]# bash create_user.sh
案例二:
[root@linux-server script]# vim while.sh
#
# Author:
while 1>0
do
cat <<-EOF
+-------------------------------------------------------------------------+
| System_tools V1.0 |
+-------------------------------------------------------------------------+
| a. Stop And Disabled Firewalld. |
| b. Disabled SELinux Secure System. |
| c. Install Apache Service. |
| d. Quit |
+-------------------------------------------------------------------------+
EOF
echo " Please input your select: " && read var
case "$var" in
"a")
systemctl stop firewalld && systemctl disable firewalld
;;
"b")
sed -ri s/SELINUX=enforcing/SELINUX=disabled/g /etc/selinux/config
;;
"c")
yum -y install httpd httpd-tools
;;
"d")
exit
;;
*)
echo "请按照上方提供的选项输入!!!"
;;
esac
if [ $? -eq 0 ];then
clear
else
echo "Warning: Your program exist ERROR!!!"
break
fi
done
[root@linux-server script]# chmod +x while.sh
[root@linux-server script]# ./while.sh
案例三嵌套循环
[root@localhost script]# vim test4.sh
for i in {1..100}
do
while [ $i -lt 50 ]
do
echo $i
#let i++
done
done
[root@localhost script]# chmod +x test4.sh
[root@localhost script]# bash test4.sh
练习题:
1.输入用户输入的参数,直到用户输入 "end" 结束循环
2.给脚本service.sh进行修改,当执行的时候要求输入(1、2、3、4、5)时安装对应的httpd、vim、wget、更换aliyum等功能,当输入错误 时提示应该输入正确的值但是不会退出。
3.建立批量删除用户脚本
# vim deluser.sh
read -p "请输入用户名: " na
read -p "请输入要删除的个数? " num
echo $num
read -p "确认要删除$na[Y|y]: " x
if [ $x = Y ] || [ $x = y ];then
for i in $(seq 1 $num )
do
echo "$i"
user=$na"$i"
id $user
if [ $? -eq 0 ];then
userdel -r $user
else
exit 9
fi
done
fi
chmod +x deluser.sh
./deluser.sh
4.编写99乘法表
xxxxxxxxxx
until 条件 #当后面的条件表达式为假的时候的才循环,为真的时候就停止了
do
循环体
done
xxxxxxxxxx
[root@linux-server script]# cat until.sh
x=1
until [ $x -ge 10 ]
do
echo $x
x=`expr $x + 1`
done
x=1
while [ ! $x -ge 10 ]
do
echo $x
x=`expr $x + 1`
done
[root@linux-server script]# chmod +x until.sh
[root@linux-server script]# ./until.sh
参数解释:
expr命令可以实现数值运算、数值或字符串比较、字符串匹配、字符串提取、字符串长度计算等功能
xxxxxxxxxx
shift命令
#位置参数可以用shift命令左移。比如shift 3表示原来的$4现在变成$1,原来的$5现在变成$2等等,原来的$1、$2、$3丢弃,$0不移动。不带参数的shift命令相当于shift 1。
#对于位置变量或命令行参数,其个数必须是确定的,或者当Shell程序不知道其个数时,可以把所有参数一起赋值给变量 $*。
若用户要求 Shell 在不知道位置变量个数的情况下,还能逐个的把参数一一处理,也就是在 $1 后为 $2,在 $2 后面为 $3 等,则需要用shift把所有参数变成$1
#测试 shift 命令(x_shift3.sh)
[root@linux-server script]# cat x_shift3.sh
shift
echo "第一个位置参数: $1"
[root@linux-server script]# bash x_shift3.sh 2 3
第一个位置参数: 3
#测试 shift 命令(x_shift.sh)
[root@linux-server script]# vim x_shift.sh
until [ $# -eq 0 ]
do
echo "第一个参数为: $1 参数个数为: $#"
shift
done
执行以上程序:
[root@linux-server script]# bash x_shift3.sh 1 2 3 4
结果显示如下:
第一个参数为: 1 参数个数为: 4
第一个参数为: 2 参数个数为: 3
第一个参数为: 3 参数个数为: 2
第一个参数为: 4 参数个数为: 1
从上可知 shift 命令每执行一次,变量的个数($#)减一,而变量值提前一位
练习:
用 until 和 shift 命令计算所有命令行参数的和。
[root@linux-server script]# vim x_shift2.sh
sum=0
until [ $# -eq 0 ]
do
sum=`expr $sum + $1`
shift
done
echo "sum is: $sum"
执行上述程序:
[root@linux-server script]# bash x_shift2.sh 3 5 7
其显示结果为:
15
continue、break、exit命令
Linux脚本中的break continue exit return
break
结束并退出本次循环
continue
在循环中不执行continue下面的代码,转而进入下一轮循环
exit
退出脚本
常带一个整数给系统,如 exit 0
检测:
[root@localhost script]# vim break.sh
for i in {1..10}
do
if [ $i -eq 7 ];then
continue
#break
#exit 34
else
echo $i
fi
echo "本次输出结束"
done
echo "脚本结束循环"
#可理解为:break是立马跳出循环;continue是跳出当前条件循环,继续下一轮条件循环;exit是直接退出整个脚本
例如:
在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell使用两个命令来实现该功能:break和continue。
break命令
break命令允许跳出所有循环(终止执行后面的所有循环)。
下面的例子中,脚本进入死循环直至用户输入数字大于5。要跳出这个循环,返回到shell提示符下,需要使用break命令。
代码如下:
[root@linux-server script]# vim break.sh
while :
do
echo -n "Input a number between 1 to 5: "
read aNum
case $aNum in
1|2|3|4|5)
echo "Your number is $aNum!"
;;
*)
echo "You do not select a number between 1 to 5, game is over!"
break
;;
esac
done
#将break注释掉观察结果
案例2:continue
continue命令
continue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。
[root@linux-server script]# bash break.sh
对上面的例子进行修改:
代码如下:
[root@linux-server script]# vim break1.sh
while : #默认为真
do
echo -n "Input a number between 1 to 5: "
read aNum
case $aNum in
1|2|3|4|5)
echo "Your number is $aNum!"
;;
*)
echo "You do not select a number between 1 to 5, game is over!"
continue
;;
esac
done
[root@linux-server script]# bash break1.sh
#运行代码发现,当输入大于5的数字时,该例中的循环不会结束.
break和exit的区别
[root@linux-server script]# vim case07.sh
while true
do
read -p "请输入[1/2]" num1
case $num1 in
1)
echo $num1
;;
2)
while true
do
read -p "再次输入[1/2]:" num2
case $num2 in
1)
echo $num2
;;
2)
break;; #将此处换成exit,再次尝试
#exit;;
esac
done
esac
done
ps:整理自己的思路,完善不足的地方
xxxxxxxxxx
#
# Author:
#可以先添加上账密验证环节
while :
do
trap ':' INT EXIT TSTP TERM HUP #拒绝ctrl+c以任何方式退出脚本。
clear
cat <<-EOF
+-------------------------------------+
| JumpServer @Version1.0 |
+-------------------------------------+
| a. WebServer Apache. |
| b. MySQL Databases Server. |
| c. PHP Development Computer. |
| d. Quit |
+-------------------------------------+
EOF
read -p "Please input your jump to server's number: " computer
case $computer in
a)
ssh jumper@192.168.161.129
;;
b)
ssh jumper@192.168.161.130
;;
c)
ssh jumper@192.168.161.131
;;
d)
exit
;;
*)
printf "ERROR: Please redo your select!"
;;
esac
done
思考:根据自己对堡垒机的解析加以修改
xxxxxxxxxx
function (功能) 功能函数
完成特定功能的代码片段
函数必须先定义才能使用
优点:避免重复的代码
定义函数---1.如何定义?
调用函数---如何使用,,分为本地调用,通过别的脚本调用函数。
取消函数----unset func_name
函数传参----和脚本传参类似
命名空间:在shell语言中命名空间函数内和函数外(不包括脚本)是一样的,函数内外不能赋值同样名字的变量
#变量:如果在同一个命名空间可以用,如果不再同一个命名空间就不能用
#分开函数和shell的命名空间:如果不让在其他空间用使用:local 分开
函数变量使用的范围:
默认,函数里的变量会在函数外面生效
local 变量名称 #变量只在函数内生效。属于局部变量
# vim var.sh
a=10
var() {
echo $a
#local a
a=20
echo $a
}
var
echo $a
返回值:return value:#value不能超过0-255,是函数里面函数最后一条执行命令的返回值,默认返回值是由这条命令执行结果确定的
[root@localhost ~]# vim return.sh
func(){
echo "hello"
return 250
if [ $? -eq 0 ];then
return 250 #返回的是执行函数的返回值
else
return 251
fi
}
func
echo $? #返回的是执行命令的返回值
if [ $? -eq 250 ];then
echo "执行成功"
elif [ $? -eq 251 ];then
echo "执行失败"
fi
exit:返回结果并退出程序
return: 返回结果并退出函数
#扩展案例:脚本回滚:
[root@localhost ~]# vim dir.sh
dir(){
for i in $(seq 1 $1)
do
mkdir /opt/dir$i
if [ $? -eq 0 ];then
echo "dir$i创建成功"
sleep 1
else
return 2
fi
done
}
dir $1
echo $?
[root@localhost ~]# bash dir.sh 9
#脚本回滚
[root@localhost ~]# cat dir.sh
dir(){
for i in $(seq 1 $1)
do
mkdir /opt/dir$i
if [ $? -eq 0 ];then
echo "dir$i创建成功"
sleep 1
else
return 2
fi
done
}
func(){
rm -rvf /opt/dir*
}
dir $1
if [ $? -eq 2 ];then
func
else
echo "脚本执行失败"
fi
[root@localhost ~]# bash dir.sh 9
函数声明
xxxxxxxxxx
function_name () {
list of commands
}
函数名 function_name,这就是你将使用它从其他地方在你的脚本调用。
取消函数
xxxxxxxxxx
unset myfunc #取消函数
xxxxxxxxxx
[root@linux-server script]# vim func.sh
myfunc(){ #定义函数
echo “This is my first shell function”
}
myfunc #函数调用
产生以下执行结果
xxxxxxxxxx
[root@linux-server script]# bash func.sh
“This is my first shell function”
xxxxxxxxxx
函数必须提前定义测试
[root@linux-server script]# vim fun01.sh
fun () {
echo "hello"
}
fun
unset fun #取消函数
fun
[root@linux-server script]# bash fun01.sh
hello
fun01.sh: line 7: fun: command not found
xxxxxxxxxx
函数的返回值,返回的是函数体内最后一条命令是否成功的返回值
[root@linux-server script]# systemctl stop httpd
[root@linux-server script]# cat fun02.sh
fun() {
systemctl status httpd &>/dev/null
systemctl status vsftpd &>/dev/null
}
fun
echo $?
[root@linux-server script]# systemctl stop vsftpd
[root@linux-server script]# bash fun02.sh
3
xxxxxxxxxx
定义函数脚本
[root@localhost script]# cat a.sh
check_net() {
echo "正在检查网络通信"
ping -c1 www.baidu.com 2&> /dev/null
if [ $? -eq 0 ];then
echo "你的网络是没有问题的"
else
echo "你的网络有问题,请先检查网络"
exit 2
fi
}
check_net
echo "+++++++++++++++++++++++++++++++++++++++++++++"
check_yum() {
echo "正在检查yum源是否可用"
yum repolist
if [ $? -eq 0 ];then
echo "你的yum源可以正常使用"
else
echo "yum源不能用,请手动配置网络yum源"
exit 3
fi
}
#check_yum
echo "+++++++++++++++++++++++++++++++++++++++++++++++++++"
install_nginx() {
#检查网络是否可以上网
check_net
echo "正在配置nginx的yum源"
cat > /etc/yum.repos.d/nginx.repo <<EOF
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/7/x86_64/
gpgcheck=0
enabled=1
EOF
yum clean all && sleep 2
yum install -y nginx 2&> /dev/null
if [ $? -eq 0 ];then
echo "nginx install successfull and start nginx"
sleep 2
systemctl start nginx
if [ $? -eq 0 ];then
echo "nginx started is success,plaess check port!"
port=`netstat -lntp | grep nginx | awk '{print $4}' | awk -F ":" '{print $NF}'`
echo "nginx is port $port"
else
echo "nginx 启动失败,请手动检查!"
exit 4
fi
else
echo "nginx install failed!将会自动退出"
exit 5
fi
}
#install_nginx
函数调用
root@localhost script]# cat b.sh #通过其他脚本调用函数脚本
while :
do
echo "这是服务器基本检测功能脚本"
cat <<EOF
++++++++++++++++++++++++++++++++++
+ 1. 检查yum源 +
+ 2. 检查网络 +
+ 3. 安装nginx +
+ 4. 退出 +
++++++++++++++++++++++++++++++++++
EOF
source ./a.sh #写你函数脚本的绝对路径,这里指的是执行函数脚本,让它为下面调用函数生效
read -p "请输入你的选项: " num
case $num in
1)
check_yum
;;
2)
check_net
;;
3)
install_nginx
;;
4)
exit;;
*)
echo "你输入的选项失败请重新输入"
esac
done
函数传参 在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参 数,$2表示第二个参数
示例
xxxxxxxxxx
[root@linux-server script]# vim fun03.sh
if [ ! $# -eq 3 ];then
echo "Must Input Three number: " p1 p2 p3
exit
fi
fun() {
echo $[$1*$2*$3]
}
fun 1 2 3 #进行传参
[root@linux-server script]# bash fun03.sh 1 3 4 6 #这个时候只是传参到了脚本,并没有传到函数里面
Must Input Three number: p1 p2 p3
修改版:
[root@linux-server script]# vim fun04.sh
fun() {
echo $[$1*$2*$3]
}
fun $1 $2 $3
[root@linux-server script]# bash fun04.sh 1 3 5
15
xxxxxxxxxx
什么是数组?
数组也是一种变量,常规变量只能保存一个值,数组可以保存多个值
#普通数组:只能用整数作为数组的索引--0 下标
#关联数组:可以使用字符串作为数组的索引
xxxxxxxxxx
普通数组定义:
[root@linux-server script]# books=( linux shell awk sed ) ---在python中叫列表
引用:${array_name[index]} #引用
[root@linux-server script]# echo ${books[0]}
linux
[root@linux-server script]# echo ${books[1]}
shell
[root@linux-server script]# echo ${books[2]}
awk
#关联数组需要提前声明
Declare命令:
[test @test test]# declare [-选项]
参数说明:
-a :#定义为数组--array
-A : #定义关联数组
例1
declare -A myarry1
[root@linux-server script]# declare -A myarry1
[root@linux-server script]# myarry1=([name]=soso666 [sex]=man [age]=18)
[root@linux-server script]# echo ${myarry1[name]}
soso666
[root@linux-server script]# echo ${myarry1[age]}
18
xxxxxxxxxx
定义方法1:
[root@linux-server script]# declare -a myarry=(5 6 7 8)
[root@linux-server script]# echo ${myarry[2]}
显示结果为 7
定义方法2:
# array=( one two three four five six )
# array2=(tom jack alice)
# array3=(`cat /etc/passwd`) #希望是将文件中的每一行作为一个值赋给数组array3
# array4=(tom jack alice "bash shell")
# array5=(1 2 3 4 5 6 7 "linux shell" [20]=saltstack)
定义方法3:
#语法:数组名[index]=变量值
area[11]=23
area[13]=37
area[51]="UFO"
示例
[root@linux-server script]# vim shuzu.sh
NAME[0]="BJ"
NAME[1]="SH"
NAME[2]="SZ"
NAME[3]="GZ"
NAME[4]="HZ"
NAME[5]="ZZ"
echo "First Index: ${NAME[0]}"
echo "Second Index: ${NAME[1]}"
echo "sixth Index: ${NAME[5]}"
输出结果
[root@linux-server script]# bash shuzu.sh
First Index: BJ
Second Index: SH
sixth Index: ZZ
当设置任何数组变量时,可以访问它
xxxxxxxxxx
[root@linux-server script]# aa=(haha heihei baibai)
[root@linux-server script]# echo ${aa[0]} #访问数组中的第一个元素
[root@linux-server script]# echo ${aa[@]} #访问数组中所有的元素 等同与echo ${aa[*]}
[root@linux-server script]# echo ${#aa[@]} #统计元素的个数
[root@linux-server script]# echo ${!aa[@]} #打印索引
您可以访问数组中的所有项目通过以下方式之一:
xxxxxxxxxx
${array_name[*]}
${array_name[@]}
示例
xxxxxxxxxx
[root@linux-server script]# vim shuzu1.sh
NAME[0]="BJ"
NAME[1]="SH"
NAME[2]="SZ"
NAME[3]="GZ"
NAME[4]="HZ"
echo "${NAME[*]}"
echo "${NAME[@]}"
输出结果
xxxxxxxxxx
[root@linux-server script]# bash shuzu1.sh
BJ SH SZ GZ HZ
BJ SH SZ GZ HZ
疑难点 shell数组中"*" 和 "@" 区别
xxxxxxxxxx
关于在shell脚本中数组变量中 “*”跟 “@” 区别
*当变量加上"" 会当成一串字符串处理.
@当变量加上"" 依然当做数组处理.
在没有加上"" 的情况下效果是等效的.
示例
xxxxxxxxxx
[root@linux-server script]# vim test1.sh
array=(gz cloud 19)
echo "case 1"
for line in "${array[@]}"
do
echo $line
done
echo "case 2"
for line in "${array[*]}"
do
echo $line
done
echo "case 3"
for line in ${array[*]}
do
echo $line
done
echo "case 4"
for line in ${array[@]}
do
echo $line
done
执行结果
xxxxxxxxxx
[root@linux-server script]# bash test1.sh
case 1
gz
cloud
19
case 2
gz cloud 19
case 3
gz
cloud
19
case 4
gz
cloud
19
xxxxxxxxxx
遍历数组---while---处理行比较擅长,不管有没有空格都是一行
将一个文件中的每一行作为数组的元素赋值给数组并遍历
[root@newrain array]# cat array01.sh
#++i:是先自加1后赋值;i++:是先赋值后自加1。 i不定义默认值0
while read line #数组赋值
do
host[i++]=$line
done </etc/hosts
echo
for i in ${!host[@]} #数组遍历,建议使用索引遍历
do
echo "$i:${host[i]}"
done
遍历数组for
[root@newrain array]# cat array02.sh
#IFS=$'\n'
for line in `cat /etc/hosts`
do
host[j++]=$line
done
for i in ${!host[@]}
do
echo ${host[i]}
done
#注意:for循环中会将tab\空格\回车作为分隔符默认为空格.
扩展:
Shell 脚本中有个变量叫IFS(Internal Field Seprator) ,内部域分隔符。IFS是一个变量,当shell处理"命令替换"和"参数替换"时,shell根据IFS的值,默认是space, tab, newline来拆解读入的变量,然后对特殊字符进行处理,最后重新组合赋值给该变量。
使用方式:
OLD_IFS=$IFS #先保存原始值
IFS="\n" #改变IFS的值
...
...
IFS=$OLD_IFS #如果用的时候在还原IFS的原始值
练习:通过数组统计shell的种类和数量--/etc/passwd
#把要统计的对象作为数组的索引,最后对他们的值进行累加--必须为关联数组
[root@localhost ~]# vim count_shells.sh
declare -A shells
while read line
do
type=`echo $line | awk -F":" '{print $NF}'`
let shells[$type]++ #匹配到之后加1
done < /etc/passwd
for i in ${!shells[@]}
do
echo "$i: ${shells[$i]}" #数组名加上索引统计出值
done
xxxxxxxxxx
正则表达式(regular expression, RE)是一种字符模式,用于在查找过程中匹配指定的字符。
在大多数程序里,正则表达式都被置于两个正斜杠之间;例如/l[oO]ve/就是由正斜杠界定的正则表达式,
它将匹配被查找的行中任何位置出现的相同模式。在正则表达式中,#元字符是最重要的概念。
#正则表达式分为:
正则表达式基本元字符
正则表达式拓展元字符
元字符使正则表达式具有处理能力。所谓元字符就是指那些在正则表达式中具有特殊意义的专用字符,可以用来规定其前导字符(即位于元字符前面的字符)在目标对象中的出现模式。
xxxxxxxxxx
基本正则表达式元字符
元字符
示例 功能 示例
^ 行首定位符 ^love
$ 行尾定位符 love$
. 匹配单个字符 l..e
* 匹配前导符0到多次 ab*love
.* 匹配任意多个字符(贪婪匹配)
[] 匹配方括号中任意一个字符 [lL]ove
[ - ] 匹配指定范围内的一个字符 [a-z0-9]ove
[^] 匹配不在指定组里的字符 [^a-z0-9]ove
\ 用来转义元字符 love\.
\< 词首定位符 \<love
\> 词尾定位符 love\>
\(\) 匹配后的标签
xxxxxxxxxx
扩展正则表达式元字符 功能 示例
+ 匹配一次或多次前导字符 [a-z]+ove
? 匹配零次或一次前导字符 lo?ve
a|b 匹配a或b love|hate
x{m} 字符x重复m次 o{5}
x{m,} 字符x重复至少m次 o{5,}
x{m,n} 字符x重复m到n次 o{5,10}
() 字符组 love(able|rs)ov ov+ (ov)+
xxxxxxxxxx
[root@linux-server ~]# num1=1
#运用正则,判断需要[[ ]]
[root@linux-server ~]# [[ $num1 =~ ^[0-9]+$ ]] && echo "yes" || echo "no"
yes
#注意:^在[]内表示取反,^在[]外表示以什么开头
[root@linux-server ~]# num3=1b1
[root@linux-server ~]# [[ $num3 =~ ^[0-9]+$ ]] && echo "yes" || echo "no"
no
[root@linux-server ~]# num=1.6
[root@linux-server ~]# [[ $num =~ ^[0-9]\.[0-9]+$ || $num =~ ^[0-9]+$ ]] && echo "yes" || echo "no" //输入的只能是数字(包括小数)
grep命令是Globally search a Regular Expression and Print的缩写,表示进行全局的正则匹配并进行打印。grep的相关扩展命令包括egrep和fgrep,其中egrep支持更多的正则匹配,fgrep只进行字符的匹配,不支持正则表达式。
xxxxxxxxxx
[root@localhost ~]# grep '^#' /etc/ssh/ssh_config #过滤以#号开头的行
[root@localhost ~]# grep -v '^#' /etc/ssh/ssh_config #-v:取反,表示反向查找
[root@localhost ~]# grep 'sendenv' /etc/ssh/ssh_config
[root@localhost ~]# grep -i 'sendenv' /etc/ssh/ssh_config #-i忽略大小写
[root@localhost ~]# grep 'bash' /opt/test/ #过滤某个目录下面带有bash的行
[root@localhost ~]# grep -r 'bash' /opt/test/ #-[r|R]表示递归查询
xxxxxxxxxx
#grep基本正则匹配
* 0或多个
[root@linux-server ~]# useradd abrt
[root@linux-server ~]# grep 'abr*' /etc/passwd
abrt:x:1041:1041::/home/abrt:/bin/bash
\< 词首定位符号 \>词尾定位符号
[root@linux-server ~]# cat jack.txt
Jack JACK JAck jackly jack
:% s/\<[Jj]ack\>/123/g
^以什么开头
[root@linux-server ~]# grep '^root' /etc/passwd
root:x:0:0:root:/root:/bin/bash
$以什么结尾
[root@linux-server ~]# grep 'bash$' /etc/passwd
root:x:0:0:root:/root:/bin/bash
confluence:x:1000:1000:Atlassian Confluence:/home/confluence:/bin/bash
to:x:1003:1003::/home/to:/bin/bash
. 匹配单个字符
[root@linux-server ~]# grep 'r..t' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
dockerroot:x:998:995:Docker User:/var/lib/docker:/sbin/nologin
[root@linux-server ~]# grep 'r.t' /etc/passwd
operator:x:11:0:operator:/root:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
.* 任意多个字符
[root@linux-server ~]# grep 'r.*t' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin polkitd:x:999:997:User for polkitd:/:/sbin/nologin postfix:x:89:89::/var/spool/postfix:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin dockerroot:x:998:995:Docker User:/var/lib/docker:/sbin/nologin
tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
abrt:x:1041:1041::/home/abrt:/bin/bash
[] 匹配方括号中的任意一个字符
[root@linux-server ~]# grep 'Root' /etc/passwd
[root@linux-server ~]# grep '[Rr]oot' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
dockerroot:x:998:995:Docker User:/var/lib/docker:/sbin/nologin
[ - ] 匹配指定范围内的一个字符
[root@linux-server ~]# grep [a-z]oot /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
dockerroot:x:998:995:Docker User:/var/lib/docker:/sbin/nologin
[^] 匹配不在指定组内的字符,非得意思
[root@linux-server ~]# grep '[^0-9]oot' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
dockerroot:x:998:995:Docker User:/var/lib/docker:/sbin/nologin
[root@linux-server ~]# grep '[^0-9A-Z]oot' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
dockerroot:x:998:995:Docker User:/var/lib/docker:/sbin/nologin
[root@linux-server ~]# grep '[^0-9A-Za-z]oot' /etc/passwd #A-Z只是大写的
[root@linux-server ~]#
[root@newrain ~]# useradd Root
[root@newrain ~]# grep '[a-z]oot' /etc/passwd #a-z
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
dockerroot:x:998:995:Docker User:/var/lib/docker:/sbin/nologin Root:x:1042:1042::/home/Root:/bin/bash
[root@linux-server ~]# grep '^[rc]oot' /etc/passwd
root:x:0:0:root:/root:/bin/bash
#注意:^在[]内表示取反,^在[]外表示以什么开头
\(\)匹配后的标签
[root@linux-server ~]# cat file1.txt
IPADDR=192.168.1.123
GATEWAY=192.168.1.1
NETMASK=255.255.255.0
DNS=114.114.114.114
:%s/\(192.168.1.\)123/\12/
:%s/\(192.\)\(168.\)\(1.\)2/\1\2\35/
:%s/\(192.\)\(168.\)\(1.\)\(5\)/\1\26.\4/
xxxxxxxxxx
#grep扩展正则匹配---egrep
扩展正则表达式元字符 功能 示例
+ 匹配一次或多次前导字符 [a-z]+ove
? 匹配零次或一次前导字符 lo?ve
a|b 匹配a或b love|hate
x{m} 字符x重复m次 o{5}
x{m,} 字符x重复至少m次 o{5,}
x{m,n} 字符x重复m到n次 o{5,10}
() 字符组 love(able|rs)ov ov+ (ov)+
egrep 支持正则表达式的拓展元字符
[root@linux-server ~]# egrep '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' /etc/resolv.conf
nameserver 192.168.246.2
+ 匹配一个或多个前导字符
[root@linux-server ~]# egrep 'ro+t' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
dockerroot:x:998:995:Docker User:/var/lib/docker:/sbin/nologin
? 匹配零次或一次前导字符
[root@linux-server ~]# egrep 'ro?t' /etc/passwd
abrt:x:1041:1041::/home/abrt:/bin/bash
a|b 匹配a或b
[root@linux-server ~]# netstat -anlp|egrep ':80|:22'
[root@linux-server ~]# egrep 'root|alice' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
dockerroot:x:998:995:Docker User:/var/lib/docker:/sbin/nologin
x{m} 字符x重复m次
[root@linux-server ~]# cat a.txt
love
love.
loove
looooove
[root@linux-server ~]# egrep 'o{2}' a.txt
loove
looooove
[root@linux-server ~]# egrep 'o{2,}' a.txt
loove
looooove
[root@linux-server ~]# egrep 'o{6,7}' a.txt
xxxxxxxxxx
总结:#没有括号看前面的一个字符
love* 出现0-n次
love? 出现0-1次
love+ 出现1-n次
love{2} 出现2次
#有括号表示一个组。
lo(ve)* 出现0-n次
lo(ve)? 出现0-1次
l(ove)+ 出现1-n次
lo(ve){4} 出现4次
sed:stream editor(流编辑器)的缩写是一种在线非交互式编辑器,它一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。
Sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等
非交互式编辑器,一次处理一行内容。
xxxxxxxxxx
支持正则表达式
与grep一样,sed在文件中查找模式时也可以使用正则表达式(RE)和各种元字符,用于查找和替换,以下是sed支持的元字符:
使用基本元字符集 ^, $, ., *, [], [^], \< \>,\(\)
使用扩展元字符集 ?, +, { }, |, ( )
xxxxxxxxxx
sed -r
#在实际使用的时候,都会加上 -r 参数,即使没有用的扩展正则也不会有任何影响。
sed 默认会输出文件的每一行,无论这行内容是否能匹配上匹配模式,参数
p
假如被匹配到的则会再输出一次。
xxxxxxxxxx
# sed -r '' file_name
# sed -r 'p' file_name
p
是 sed 的内部命令,是 打印(输出) 的作用
sed会自动打印文件的每一行,同时查找模式匹配的行,找到后执行后面的命令,默认是 p
打印(不加 -n
的情况下)
xxxxxxxxxx
示例文件
[root@localhost ~]# vim test.txt
MA Daggett, 341 King Road, Plymouth MA
Alice Ford, 22 East Broadway, Richmond VA
MA Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Ma Road, mA Falls PA
Eric Adams, 20 Post Road, Sudbury mA
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Ma Pkwy, Mountain View CA
Sal Carpenter, 73 MA Street, Boston MA
1.搜索每一行匹配到MA的将第一个替换为Massachusetts:
[root@localhost ~]# sed -r 's/MA/Massachusetts/' test.txt
s:----查找
2.搜索每一行,找到所有的MA字符,进行全局替换为Massachusetts
[root@localhost ~]# sed -r 's/MA/Massachusetts/g' test.txt
3.搜索每一行,找到所有的MA字符,进行全局替换为Massachusetts同时忽略大小写
[root@localhost ~]# sed -r 's/MA/Massachusetts/gi' test.txt
4.-n #静默输出(不打印默认输出)
[root@localhost ~]# sed -r -n 's/MA/Massachusetts/' test.txt
案例:
[root@localhost ~]# sed -r 's/SELINUX=disabled/SELINUX=enabled/' /etc/sysconfig/selinux
xxxxxxxxxx
1.使用多重指令:-e 给予sed多个命令的时候需要-e选项
[root@localhost ~]# sed -r -e 's/MA/Massachusetts/' -e 's/PA/Pennsylvania/' test.txt
2.使用脚本文件:当有多个要编辑的项目时,可以将编辑命令放进一个脚本里,再使用sed搭配-f选项
-f<script文件>或--file=<script文件> 以选项中指定的script文件来处理输入的文本文件。
[root@localhost ~]# vim s.sed
s/MA/Massachusetts/
s/PA/Pennsylvania/
s/CA/California/
s/VA/Virginia/
s/OK/Oklahoma/
[root@localhost ~]# sed -f s.sed test.txt
保存输出:
[root@localhost ~]# sed -f s.sed test.txt > newfile.txt
地址用于决定对哪些 行
进行编辑。地址形式可以是数字、正则表达式或二者的结合。如果没有指定地址,sed将处理输入文件中的所有行。
xxxxxxxxxx
[root@localhost ~]# head /etc/passwd > passwd #生成测试文件
[root@localhost ~]# sed -r '1d' passwd #d:表示删除-- 删除文件的第1行
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@localhost ~]# sed -r '1,2d' passwd #删除文件的1-2行 daemon:x:2:2:daemon:/sbin:/sbin/nologin
创建测试文件:
[root@localhost ~]# cat e.txt
/etc/abc/123
etc
#查找替换:
[root@localhost ~]# sed -r 's#/etc/abc#/var/lib#' e.txt
/var/lib/123
etc
[root@localhost ~]# cat passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
...
[root@localhost ~]# sed -r '2,$d' passwd #删除第2行到最后一行
root:x:0:0:root:/root:/bin/bash
[root@localhost ~]# sed -r '/^root/d' passwd #匹配到root开头的行,删除此行
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@localhost ~]# sed -r '/root/d' passwd #含有root的行都删除
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@localhost ~]# sed -r '/bash/,3d' passwd #匹配到bash行,到此行的第3行删除
adm:x:3:4:adm:/var/adm:/sbin/nologin
[root@localhost ~]# cat -n passwd
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8 halt:x:7:0:halt:/sbin:/sbin/halt
9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10 operator:x:11:0:operator:/root:/sbin/nologin
[root@localhost ~]# sed -r '1~2d' passwd #删除奇数行,间隔两行删除
bin:x:1:1:bin:/bin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
halt:x:7:0:halt:/sbin:/sbin/halt
operator:x:11:0:operator:/root:/sbin/nologin
[root@localhost ~]# sed '0~2d' passwd #删除偶数行,从0开始间隔2行删除
passwd root:x:0:0:root:/root:/bin/bash
daemon:x:2:2:daemon:/sbin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
xxxxxxxxxx
1.插入命令--i
[root@localhost ~]# sed -r '2i\222222' passwd #在第2行插入
[root@localhost ~]# sed -r '2i222222\
> 3333333\
> 4444444' passwd
2.修改命令---c
[root@localhost ~]# sed -r '4c\asfasdf' passwd
[root@localhost ~]# sed -r '4c\11111111\
> aaaaaaaaaa\
> bbbbbbbbb' passwd
案例:
[root@localhost ~]# sed -r '7c\SELINUX=enabled' /etc/sysconfig/selinux
3.选项 -i 会使得sed用修改后的数据替换原文件
[root@localhost ~]# sed -r -i '7c\SELINUX=enabled' /etc/sysconfig/selinux #修改
[root@localhost ~]# sed -r -i 's/SELINUX=enabled/SELINUX=disabled/' /etc/sysconfig/selinux #替换
4.由于在使用 -i 参数时比较危险, 所以我们在使用i参数时在后面加上.bak就会产生一个备份的文件,以防后悔
[root@localhost ~]# sed -r -i.bak 's/root/ROOT/' passwd
5.sed如果在脚本中使用的话, 不可避免的要调用变量, 所以以下这种方式可以用来调用变量
test=hello
echo "$test world" | sed "s/$test/HELLO/"
xxxxxxxxxx
[root@localhost ~]# cp /etc/ssh/ssh_config .
1.删除配置文件中 # 号注释的行
[root@localhost ~]# sed -ri '/^#/d' ssh_config
2.给文件行添加注释:
[root@localhost ~]# sed -r '2,5s/^/#/' passwd
3.给文件行添加和取消注释
[root@localhost ~]# sed -ri s/^#baseurl/baseurl/g /etc/yum.repos.d/CentOS-Base.repo
[root@localhost ~]# sed -r s/^mirrorlist/#mirrorlist/g /etc/yum.repos.d/CentOS-Base.repo
4.删除配置文件中//号注释行
[root@qfedu ~]# sed -r '\#^//#d' passwd
5.删除配置文件中tab键开头的行
[root@qfedu ~]# sed -r '\#^[ \t]#d' passwd
awk 是一种编程语言,用于在linux/unix下对文本和数据进行处理。数据可以来自标准输入、一个或多个文件,或其它命令的输出。可以在命令行中使用,但更多是作为脚本来使用。
awk的处理文本和数据的方式是这样的,它逐行扫描文件,从第一行到最后一行,寻找匹配的特定模式的行,并在这些行上进行操作。如果没有指定处理动作,则把匹配的行显示到标准输出(屏幕),如果没有指定模式,则所有被操作所指定的行都被处理。
awk分别代表其作者姓氏的第一个字母。因为它的作者是三个人,分别是Alfred Aho、Brian Kernighan、Peter Weinberger。
xxxxxxxxxx
awk是行处理器: 优势在于处理庞大文件时不会出现内存溢出或是处理缓慢的问题。
awk处理过程: 依次对每一行进行处理,然后输出,默认分隔符是空格或者tab键
awk [options] 'commands' filenames
options:
-F
对于每次处理的内容,可以指定一个自定义的分隔符,默认的分隔符是空白字符(空格或 tab 键 )
awk -F":" '{print $1,$3}' /etc/passwd
xxxxxxxxxx
(1)awk使用一行作为输入,并将这一行赋给变量$0,每一行可称作为一个记录,以换行符结束
(2)然后,行被空格分解成字段,每个字段存储在已编号的变量中,从$1开始
(3)awk如何知道空格来分隔字段的呢?因为有一个内部变量FS来确定字段分隔符,初始时,FS赋为空格或者是tab
(4)awk打印字段时,将以设置的方法,使用print函数打印,awk在打印的字段间加上空格,因为$1,$2间有一个,逗号。逗号比较特殊,映射为另一个变量,成为输出字段分隔符OFS,OFS默认为空格
(5)awk打印字段时,将从文件中获取另一行,并将其存储在$0中,覆盖原来的内容,然后将新的字符串分隔成字段并进行处理。该过程持续到处理文件结束。
command:
xxxxxxxxxx
BEGIN{} {} END{} filename
行处理前的动作 行内容处理的动作 行处理之后的动作 文件名
BEGIN{}和END{} 是可选项。
函数-BEGIN{}:读入文本之前要操作的命令。(也可以设置变量.取值可以不加$)
{}:主输入循环:用的最多。读入文件之后擦操作的命令。如果不读入文件都可以不用写。
END{}:文本全部读入完成之后执行的命令。
xxxxxxxxxx
[root@awk ~]# awk 'BEGIN{ print 1/2} {print "ok"} END{print "----"}' /etc/hosts
或者:
[root@awk ~]# cat /etc/hosts | awk 'BEGIN{print 1/2} {print "ok"} END{print "----"}'
0.5
ok
ok
----
xxxxxxxxxx
1.记录和字段
awk 按记录处理:一行是一条记录,因为awk默认以换行符分开的字符串是一条记录。(默认\n换行符:记录分隔符)
字段:以字段分割符分割的字符串 默认是单个或多个“ ” tab键。
2.awk中的变量
$0:表示整行;
NF : 统计字段的个数
$NF:是number finally,表示最后一列的信息
RS:输入记录分隔符;
ORS:输出记录分隔符。
NR:打印记录号,(行号)
FNR:可以分开,按不同的文件打印行号。
FS : 输入字段分隔符,默认为一个空格。
OFS 输出的字段分隔符,默认为一个空格。
FILENAME 文件名 被处理的文件名称
$1 第一个字段,$2第二个字段,依次类推...
x
FS(输入字段分隔符)---一般简写为-F(属于行处理前)
[root@awk ~]# cat /etc/passwd | awk 'BEGIN{FS=":"} {print $1,$2}'
root x
bin x
daemon x
adm x
lp x
sync x
shutdown x
halt x
mail x
[root@awk ~]# cat /etc/passwd | awk -F":" '{print $1,$2}'
root x
bin x
daemon x
adm x
lp x
sync x
shutdown x
halt x
mail x
#注:如果-F不加默认为空格区分!
===============================================================
OFS(输出字段分隔符)
[root@awk ~]# cat /etc/passwd | awk 'BEGIN{FS=":";OFS=".."} {print $1,$2}'
root..x
bin..x
daemon..x
adm..x
lp..x
sync..x
shutdown..x
halt..x
mail..x
======================================================================
1.创建两个文件
[root@awk ~]# vim a.txt
love
love.
loove
looooove
[root@awk ~]# vim file1.txt
isuo
IPADDR=192.168.246.211
hjahj123
GATEWAY=192.168.246.1
NETMASK=255.255.255.0
DNS=114.114.114.114
NR 表示记录编号, 在awk将行做为记录, 该变量相当于当前行号,也就是记录号
[root@awk ~]# awk '{print NR,$0}' a.txt file1.txt
1 love
2 love.
3 loove
4 looooove
5
6 isuo
7 IPADDR=192.168.246.211
8 hjahj123
9 GATEWAY=192.168.246.1
10 NETMASK=255.255.255.0
11 DNS=114.114.114.114
FNR:表示记录编号, 在awk将行做为记录, 该变量相当于当前行号,也就是记录号(#会将不同文件分开)
[root@awk ~]# awk '{print FNR,$0}' a.txt file1.txt
1 love
2 love.
3 loove
4 looooove
5
1 isuo
2 IPADDR=192.168.246.211
3 hjahj123
4 GATEWAY=192.168.246.1
5 NETMASK=255.255.255.0
6 DNS=114.114.114.114
===========================================================
RS(输入记录分隔符)
1.创建一个文件
[root@awk ~]# vim passwd
root:x:0:0:root:/root:/bin/bashbin:x:1:1:bin:/bin:/sbin/nologin
[root@awk ~]# cat passwd | awk 'BEGIN{RS="bash"} {print $0}'
root:x:0:0:root:/root:/bin/
bin:x:1:1:bin:/bin:/sbin/nologin
ORS(输出记录分隔符)
2.对刚才的文件进行修改
[root@awk ~]# vim passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
[root@awk ~]# cat passwd | awk 'BEGIN{ORS=" "} {print $0}'
root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin
===========================================================
NF:统计列的个数
[root@awk ~]# cat /etc/passwd | awk -F":" '{print NF}'
7
7
7
7
$NF:打印最后一列
[root@awk ~]# cat /etc/passwd | awk -F":" '{print $NF}'
/bin/bash
/sbin/nologin
/sbin/nologin
/sbin/nologin
/sbin/nologin
xxxxxxxxxx
练习1:将文件合并为一行
[root@awk ~]# cat /etc/passwd | awk 'BEGIN{ORS="" } {print $0}'
练习2:把一行内容分成多行
1.首先创建一个文件
[root@awk ~]# vim d.txt
root:x:0:0:root:/root:/bin/bash
[root@awk ~]# cat d.txt | awk 'BEGIN{RS=":"} {print $0}'
root
x
0
0
root
/root
实现 字符串的完全相等需要使用 ==
字符串需要使用双引号
!=
表示不等于
xxxxxxxxxx
[root@awk ~]# awk -F":" '$NF == "/bin/bash"' /etc/passwd
[root@awk ~]# awk -F":" '$1 != "root"' /etc/passwd
比较表达式:
比较表达式采用对文本进行比较,只有当条件为真,才执行指定的动作。 比较表达式使用关系运算符,用于比较数字与字符串。
关系运算符有
<
小于 例如x<y
>
大于x>y
<=
小于或等于x<=y
==
等于x==y
!=
不等于x!=y
>=
大于等于x>=y
xxxxxxxxxx
[root@awk ~]# awk -F":" '$3 == 0' /etc/passwd
[root@awk ~]# awk -F":" '$3 < 10' /etc/passwd
算术运算:+
, -
, *
, /
, %(模: 取余)
, ^(幂:2^3)
可以在模式中执行计算,awk都将按浮点数方式执行算术运算
xxxxxxxxxx
awk -F: '$3 * 10 > 500' /etc/passwd
逻辑操作符和复合模式
&&
逻辑与, 相当于 并且||
逻辑或,相当于 或者!
逻辑非 , 取反
xxxxxxxxxx
awk -F":" '$1~/root/ && $3<=15' /etc/passwd
awk -F":" '$1~/root/ || $3<=15' /etc/passwd
1.打印一个文件中的第2列和第5列
xxxxxxxxxx
# cat /etc/passwd | awk -F : '{print $2,$5}'
2.打印指定行指定列的某个字符
xxxxxxxxxx
# free -m | awk 'NR==2 {print $2}'
3.统计一个文件的行数
xxxxxxxxxx
# cat /etc/passwd | awk '{print NR}'
获取根分区的使用量
4.在awk中使用if条件判断
xxxxxxxxxx
++i:从1开始加,赋值在运算
i++: 从0开始加,运算在赋值
if语句:
{if(表达式){语句;语句;...}}
实战案例:
显示管理员用户姓名
[root@qfedu ~]# cat /etc/passwd | awk -F":" '{if($3==0) {print $1 " is administrator"}}'
统计系统用户数量
[root@qfedu ~]# cat /etc/passwd | awk -F":" '{if($3>=0 && $3<=1000){i++}} END{print i}'
5.在awk中使用for循环
xxxxxxxxxx
每行打印两遍
[root@qfedu ~]# awk '{for(i=1;i<=2;i++) {print $0}}' /etc/passwd
root:x:0:0:root:/root:/bin/bash
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
数组遍历--用来统计网站日志,tcp连接状态等。
xxxxxxxxxx
++i:从1开始加,运算在赋值
i++: 从0开始加,赋值在运算
#按索引遍历:
1.先创建一个test文件
# vim test.txt #将文件内容的第一个字段作为数组的值,通过索引获取到值
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
# cat test.txt | awk -F":" '{username[x++]=$1} END{for(i in username) {print i,username[i]}}'
0 root
1 bin
#注意:变量i是索引
xxxxxxxxxx
#把要统计的对象作为索引,最后对他们的值进行累加,累加出来的这个值就是你的统计数量
1. 统计/etc/passwd中各种类型shell的数量
# cat /etc/passwd | awk -F: '{shells[$NF]++} END{ for(i in shells){print i,shells[i]} }'
2.统计nginx日志出现的状态码
# cat access.log | awk '{stat[$9]++} END{for(i in stat){print i,stat[i]}}'
3.统计当前nginx日志中每个ip访问的数量
# cat access.log | awk '{ips[$1]++} END{for(i in ips){print i,ips[i]}}'
4.统计某一天的nginx日志中的不同ip的访问量
# cat access.log |grep '28/Sep/2019' | awk '{ips[$1]++} END{for(i in ips){print i,ips[i]}}'
5.统计nginx日志中某一天访问最多的前10个ip
# cat access.log |grep '28/Sep/2019' | awk '{ips[$1]++} END{for(i in ips){print i,ips[i]}}' |sort -k2 -rn | head -n 2
sort:排序,默认升序
-k:指定列数
-r:降序
-n:以数值来排序
6.统计tcp连接的状态---下去自己查各个状态,包括什么原因造成的!
# netstat -n | awk '/^tcp/ {tcps[$NF]++} END {for(i in tcps) {print i, tcps[i]}}'
LAST_ACK 5 (正在等待处理的请求数)
SYN_RECV 30
ESTABLISHED 1597 (正常数据传输状态)
FIN_WAIT1 51
FIN_WAIT2 504
TIME_WAIT 1057 (处理完毕,等待超时结束的请求数)
xxxxxxxxxx
UV与PV统计
PV:即访问量,也就是访问您商铺的次数;
例如:今天显示有300 PV,则证明今天你的商铺被访问了300次。
================================================================
UV:即访问人数,也就是有多少人来过您的商铺; #需要去重
例如:今天显示有50 UV,则证明今天有50个人来过你的商铺。
=================================================================
1.根据访问IP统计UV
# cat access.log | awk '{print $1}' |sort |uniq -c | wc -l
uniq:去重
-c:统计每行连续出现的次数
2.更具访问ip统计PV
# cat access.log | awk '{print $1}' |wc -l
或者是url
# cat access.log | awk '{print $7}' |wc -l
3.查询访问最频繁的URL
# cat access.log | awk '{print $7}'|sort | uniq -c |sort -n -k 1 -r | more
4.查询访问最频繁的IP
# cat access.log | awk '{print $1}'|sort | uniq -c |sort -n -k 1 -r | more
作业---切割nginx的日志,统计PV\UV,出现次数最多的url等各种切割统计
[root@qfedu ~] yum -y install expect
实现批量修改密码,批量推送ssh的公钥,进行远程ssh连接,任何批量操作的基本都能处理。
是一个免费的编程工具, 用来实现自动的交互式任务, 而无需人为干预. 说白了 expect 就是一套用来实现自动交互功能的软件
在实际工作中我们运行命令、脚本或程序时, 都需要从终端输入某些继续运行的指令,而这些输 入都需要人为的手工进行. 而利用 expect 则可以根据程序的提示, 模拟标准输入提供给程序, 从而实现自动化交互执 行. 这就是 expect
如果能够在工作中熟练的使用Shell脚本就可以很大程度的提高工作效率, 那么再搭配上expect这个时候很多工作都可以实现自动化进行。
xxxxxxxxxx
用法:
1)定义expect脚本执行的shell
#!/usr/bin/expect -----类似于#!/bin/bash
2)set timeout 30
设置超时时间30s
3)spawn
spawn是执行expect之后后执行的内部命令开启一个会话 #功能:用来执行shell的交互命令
4)expect ---相当于捕捉
功能:判断输出结果是否包含某项字符串(相当于捕捉返回的结果),没有则会断开,否则等待一段时间后返回,等待通过timeout设置
5)send
执行交互动作,将交互要执行的命令进行发送给交互指令,命令字符串结尾要加上“\r”,#---相当于回车
6)interract
执行完后保持交互状态,需要等待手动退出交互状态,如果不加这一项,交互完成会自动退出
7)exp_continue
继续执行接下来的操作
xxxxxxxxxx
案例1:普通操作
[root@qfedu script]# vim expect01.sh
spawn ssh root@192.168.246.115
expect {
"yes/no" { send "yes\r"; exp_continue }
"password:" { send "1\r" };
}
interact
[root@qfedu script]# chmod +x expect01.sh
[root@qfedu script]# ./expect01.sh
spawn ssh root@192.168.246.115
root@192.168.246.115's password:
Last login: Fri Aug 28 16:57:09 2019
#如果添加interact参数将会等待我们手动交互进行退出。如果不加interact参数在登录成功之后会立刻退出。
============================================================================
2.设置变量与进行传参的方式
#注意:expect环境中设置变量用set,识别不了bash方式定义的变量
[root@qfedu script]# vim expect01.sh
set user root
set pass 1
set ip [ lindex $argv 0 ] #expect固定写法,从0开始,第一个位置参数,相当于shell中的$1
set timeout 10
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\r"; exp_continue }
"password:" { send "$pass\r" };
}
interact
[root@qfedu script]# ./expect01.sh 192.168.246.115
spawn ssh root@192.168.246.115
root@192.168.246.115's password:
Last login: Fri Aug 28 07:13:57 2019 from 192.168.246.135
#如果想登录成功自动结束交互模式也就是expect,可以采用下面方式:
expect "#"
send "useradd test\r"
send "pwd\r"
send "exit\r"
expect eof #直接退出expect模式
============================================================================
3.进行批量推送公钥实现免密连接,ping通一个ip地址连接一个ip------
[root@qfedu script]# vim getip_push.sh
pass=1
#判断expect命令是否安装
rpm -qa expect &> /dev/null
if [ $? -ne 0 ];then
yum install -y expect
fi
#判断主机下面是否生成秘钥,如果没有生成秘钥
if [ ! -f ~/.ssh/id_rsa ];then
ssh-keygen -P "" -f ~/.ssh/id_rsa
fi
#循环执行获取up状态的ip地址。
for i in {2..254}
do
{
ip=192.168.198.$i
ping -c1 $ip &> /dev/null
if [ $? -eq 0 ];then
echo "$ip" >> up_ip.txt
set timeout 10
/usr/bin/expect <<-EOF #shell脚本中调用expect命令
spawn ssh-copy-id $ip
expect {
"yes/no" { send "yes\r"; exp_continue }
"password:" { send "$pass\r" };
}
expect eof
EOF
fi
} &
wait
done
echo "finish..."
[root@qfedu script]# chmod +x getip_push.sh
[root@qfedu script]# ./getip_push.sh
测试....
xxxxxxxxxx
• 实战项目1: 实现网络配置
• 实战项目2: sed实现sshd配置
• 实战项目3: sed实现nginx配置修改端口为8080
• 实战项目4: sed实现关闭本机SELinux的功能(/etc/sysconfig/selinux)
• 实战项目5: awk统计/etc/passwd各种shell数量
• 实战项目6: awk统计网站访问各种状态数量
• 实战项目7: awk统计访问的每个IP的数量
• 实战项目8: 统计Nginx日志中某一天的PV量
• 实战项目9: 获取获得内存使用情况
• 实战项目10: 基于时间的备份脚本
• 实战项目11: Web日志访问量分析程序(PV、UV)
• 实战项目12: 编写系统初始化脚本
1)设置时区并把同步时间加入计划任务
2)禁用selinux
3)历史命令显示操作时间
4)创建ALL权限用户并禁止root远程登录
5)安装常用工具
• 实战项目13: Linux系统状态收集及分析(内存使用量,硬盘使用量,cpu使用量等)
项目作为作业