应用场景
自动化批量系统初始化程序(update、软件安装、时区设置、安全策略……)
自动化批量软件部署程序(LAMP、LNMP、Tomcat、LVS、Nginx)
管理应用程序(KVM,集群管理扩容,MySQL,dellr720批量raid)
日志分析处理程序(PV,UV,200,!200,top 100,grep,awk)
自动化备份恢复程序(MySQL完全备份,增量备份,crond)
自动化管理程序(批量远程修改密码,软件升级,配置更新)
自动化信息采集及监控程序(收集系统/应用状态信息,CPU,mem,disk,apache,mysql,tcp status )
自动化扩容(增加云主机->业务上线)
配合zabbix信息采集
可以写游戏
一些基本符号
重定向输出:
>:只收集前面命令的正确信息
2>:只收集前面命令的错误信息
&>:收集前面命令的错误信息与正确信息
cat /etc /opt/1.txt > /opt/a.txt #只收集正确信息
cat /etc /opt/1.txt 2> /opt/a.txt #只收集错误信息
cat /etc /opt/1.txt &> /opt/a.txt #正确错误都收集
黑洞设备:/dev/null
[root@server0 ~]#vim /root/user.sh
#!/bin/bash
useradd nsd05 &> /dev/null
echo 用户nsd05创建成功
echo 123 | passwd --stdin nsd05 &> /dev/null
echo 用户nsd05密码设置成功
[root@server0 ~]# chmod +x /root/user.sh
[root@server0 ~]# /root/user.sh
read -p '屏幕输出信息' 变量
作用:1. 可以产生交互 2.记录用户在键盘上的输入
3.将用户在键盘上的输入内容赋值给变量储存
[root@server0 ~]# vim /root/user.sh
#!/bin/bash
read -p '请输入您要创建的用户名:' a
useradd $a &> /dev/null
echo 用户$a创建成功
echo 123 | passwd --stdin $a &> /dev/null
echo 用户$a密码设置成功
单引 ' ' :取消所有特殊字符含义 “”:界定字符串
[root@server0 ~]# echo '$a'
$( )或 反撇号 ` `:将命令的输出,作为参数参与下一个命令执行
%F 完整日期格式,等价于 年-月-日
[root@server0 opt]# date +%F
[root@server0 opt]# mkdir nsd1809-$(date +%F)
[root@server0 opt]# mkdir $(date +%F)
[root@server0 opt]# mkdir ab-`date +%F`
[root@server0 opt]# mkdir nb-`hostname`
造数机制: {起始值..结束值}
{1..10}: 1到10 所有的数字 {20..38}: 20到38所有的数字
[root@server0 ~]# touch /opt/{1..20}.txt
[root@server0 ~]# vim /root/for02.sh
#!/bin/bash
for i in {1..15}
do
echo 'I Love Dc !!!' $i
done
案例:编写一个判断脚本
在 server0 上创建 /root/foo.sh 脚本
1)当运行/root/foo.sh redhat,输出为fedora
2)当运行/root/foo.sh fedora,输出为redhat
3)当没有任何参数或者参数不是 redhat 或者fedora时,
其错误输出产生以下信息: /root/foo.sh redhat|fedora
[root@server0 ~]# vim /root/foo.sh
#!/bin/bash
if [ $# -eq 0 ];then #判断用户是否输入参数
echo '/root/foo.sh redhat|fedora' >&2 #变成错误输出
exit 1 #程序退出状态码返回值为1
elif [ $1 == redhat ];then
echo fedora
elif [ $1 == fedora ];then
echo redhat
else
echo '/root/foo.sh redhat|fedora' >&2 #变成错误输出
exit 2 #程序退出返回值为2
fi
RANDOM随机数
随机密码的案例:
[root@svr5 ~]# vim rand.sh
#!/bin/bash
x=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 // 所有密码的可能性是26+26+10=62(0-61是62个数字)
pass=''
for i in {1..8}
do
num=$[RANDOM%62]
tmp=${x:num:1}
pass=${pass}$tmp
done
echo $pass
变量
什么是变量:
以不变的名称存放的可能会变化的值
变量名=变量值
方便以固定名称重复使用某个值
提高对任务需求、运行环境变化的适应能力
变量名只能由字母/数字/下划线组成,区分大小写
变量名不能以数字开头,不要使用关键字和特殊字符
A 2004file B tou_on1 C a$b D 1abc
设置变量时的注意事项
若指定的变量名已存在,相当于为此变量重新赋值
等号两边不要有空格
基本格式:
引用变量值:$变量名
查看变量值:echo $变量名、echo ${变量名}
[root@server0 ~]# a=rhel
[root@server0 ~]# echo $a
[root@server0 ~]# echo $a7 #输出为空,会将a7作为整体
[root@server0 ~]# echo ${a}7
[root@server0 ~]# echo ${a}6
环境变量:
变量名一般都大写,用来设置用户/系统环境,系统定义赋值完成,用户直接使用即可
PATH 决定了shell将到哪些目录中寻找命令或程序
HOME 当前用户主目录
HISTSIZE 历史记录数
LOGNAME 当前用户的登录名
HOSTNAME 指主机的名称
SHELL 前用户Shell类型
LANGUGE 语言相关的环境变量,多语言可以修改此环境变量
MAIL 当前用户的邮件存放目录
PS1 基本提示符,对于root用户是#,对于普通用户是$
PS2 附属提示符,默认是“>”
USER 永远储存当前登陆的用户名
RANDOM 0-32767的随机数
位置变量:
bash内置,存储执行脚本时提供的命令行参数(非交互)
系统定义赋值完成,用户直接使用即可
[root@server0 /]# vim /root/user.sh
#!/bin/bash
useradd $1 &> /dev/null
echo 用户$1创建成功
echo 123 | passwd --stdin $1 &> /dev/null
echo 用户$1密码设置成功
[root@server0 /]# /root/user.sh nsd12
[root@server0 /]# vim /root/1.sh
#!/bin/bash
echo $1
echo $2
echo $3
[root@server0 /]# /root/1.sh test01 abc haha
[root@server0 /]# vim /root/1.sh
[root@server0 /]# cat /root/1.sh
#!/bin/bash
echo $1
cat -n $1 | head -$2
[root@server0 /]# /root/1.sh /etc/passwd 3
预定义变量:
bash内置,可直接调用的特殊值,不能直接修改,系统定义赋值完成,用户直接使用即可
$# 已加载的位置变量的个数
$*、$@ 所有位置变量的值
$$ PID进程号
$? 程序退出后的状态值,0表示正常,其他值异常
$0 当前脚本名字
$! 上一个后台进程的PID
[root@server0 /]# vim /root/1.sh
[root@server0 /]# cat /root/1.sh
#!/bin/bash
echo $1
echo $2
echo $3
echo $# #统计一共输入了几个命令行参数
echo $* #输出所有的命令行参数
[root@server0 /]# /root/1.sh 100 200 test
[root@server0 /]# cat /etc/redhat-release
Red Hat Enterprise Linux Server release 7.0 (Maipo)
[root@server0 /]# echo $?
[root@server0 /]# cat /etc/
cat: /etc/: 是一个目录
[root@server0 /]# echo $?
export 变量名 发布全局变量
export 变量名=“附值” 定义新的全局变量
运行脚本方式:
bash 脚本(生成一个子shell执行脚本)
./脚本(在子shell中执行脚本)
绝对路径执行脚本(在子shell中执行)
source 脚本(在原shell中执行脚本)
.脚本(在原shell中执行脚本)
撤消变量:unset 变量
stty -echo 关闭回显
stty echo 打开回显
运算工具
整数运算:
1)使用expr命令
乘法操作应采用 * 转义,避免被作为Shell通配符;参与运算的整数值与运算操作符之间需要以空格分开,引用变量时必须加$符号。
[root@svr5 ~]# X=1234 //定义变量X
[root@svr5 ~]# expr
X
+
78
/
/
加
法
2
)
使
用
X + 78 //加法 2)使用
X+78//加法2)使用[]或$(())表达式
乘法操作*无需转义,运算符两侧可以无空格;引用变量可省略 $ 符号;计算结果替换表达式本身,可结合echo命令输出。
[root@svr5 ~]# X=1234
[root@svr5 ~]# echo $[X+78]
3)使用let命令
expr或
[
]
、
[]、
[]、(())方式只进行运算,并不会改变变量的值;而let命令可以直接对变量值做运算再保存新的值。
[root@svr5 ~]# let X++; echo $X # X++(X=X+1)
小数运算:
1)bc交互式运算
先执行bc命令进入交互环境,然后再输入需要计算的表达式。
2)bc非交互式运算
将需要运算的表达式通过管道操作交给bc运算。注意,小数位的长度可采用scale=N限制,除此以外也受参与运算的数值的小数位影响。
[root@svr5 ~]# echo ‘scale=4;12.34+5.678/1’ | bc
提取当前登录的用户数 who | wc -l
逻辑运算:
1)&&,逻辑与
给定条件必须都成立,整个测试结果才为真。
2)||,逻辑或
只要其中一个条件成立,则整个测试结果为真。
[root@svr5 ~]# ping -c 3 -i 0.2 -W 1 192.168.4.5发送3个测试包(-c 3)发送测试包的间隔秒数(-i 0.2)等待反馈的超时秒数(-W 1)
字符串操作
子串截取的三种用法:
1)${变量名:起始位置:长度}
phone=“13788768897”
echo ${phone:0:6}
echo ${phone::6}
echo
p
h
o
n
e
:
1
:
6
2
)
e
x
p
r
s
u
b
s
t
r
"
{phone:1:6} 2)expr substr "
phone:1:62)exprsubstr"变量名" 起始位置 长度
使用expr substr截取字符串时,起始编号从1开始,这个要注意与
相
区
分
。
e
x
p
r
s
u
b
s
t
r
"
{}相区分。 expr substr "
相区分。exprsubstr"phone" 1 6
3)echo $变量名 | cut -b 起始位置-结束位置
选项 -b 表示按字节截取字符,其中起始位置、结束位置都可以省略。当省略起始位置时,视为从第1个字符开始(编号也是从1开始,与expr substr类似),当省略结束位置时,视为截取到最后。
echo $phone | cut -b 1-6
只截取单个字符,比如第9个字符: echo
p
h
o
n
e
∣
c
u
t
−
b
9
截
取
不
连
续
的
字
符
,
比
如
第
3
、
5
、
8
个
字
符
:
e
c
h
o
、
phone | cut -b 9 截取不连续的字符,比如第3、5、8个字符: echo 、
phone∣cut−b9截取不连续的字符,比如第3、5、8个字符:echo、phone | cut -b 3,5,8
字符串的替换:
1)只替换第1个子串
格式:${变量名/old/new}
将字符串中的第1个8替换为X:echo ${phone/8/X}
2)替换全部子串
格式:${变量名//old/new}
将phone字符串中的所有8都替换为X:echo ${phone//8/X}
字符串的匹配删除:
A=`head -1 /etc/passwd`
1)从左向右,最短匹配删除
格式:${变量名#*关键词}
删除从左侧第1个字符到最近的关键词“:”的部分,* 作通配符理解:echo ${A#:}
2)从左向右,最长匹配删除
格式:${变量名##*关键词}
删除从左侧第1个字符到最远的关键词“:”的部分:echo ${A##:}
3)从右向左,最短匹配删除
格式:${变量名%关键词*}
删除从右侧最后1个字符到往左最近的关键词“:”的部分,* 做通配符理解:echo ${A%:\*}
4)从右向左,最长匹配删除
格式:${变量名%%关键词*}
删除从右侧最后1个字符到往左最远的关键词“:”的部分:echo ${A%%:\*}
实例:
1)批量修改文件扩展名的脚本
脚本用途为:批量修改当前目录下的文件扩展名,将.doc改为.txt。
脚本内容参考如下:
[root@svr5 rendir]# vim renfile.sh
#!/bin/bash
for i in `ls *.doc` #注意这里有反引号
do
mv $i ${i%.*}.txt
done
2)改进版脚本(批量修改扩展名)
通过位置变量 $1、$2提供更灵活的脚本,改进的脚本编写参考如下:
[root@svr5 rendir]# vim ./renfile.sh
#!/bin/bash
#version:2
for i in `ls *.$1`
do
mv $i ${i%.*}.$2
done
字符串初值的处理:
通过${变量:-预设值}判断变量是否存在,决定变量的初始值
若变量var已存在且非Null,则返回 $var 的值;否则返回字串“word”,原变量var的值不受影响。
[root@svr5 ~]# vim sumx.sh
#!/bin/bash
read -p "请输入一个正整数:" x
x=${x:-1}
i=1; SUM=0
while [ $i -le $x ]
do
let SUM+=i
let i++
done
echo "从1到$x的总和是:$SUM"
[root@svr5 ~]# chmod +x sumx.sh
条件测试
[ 测试表达式 ] 条件表达式每一部分都要有空格
检查文件状态
-e:文档存在为真
-d:文档存在,且必须为目录才为真
-f:文档存在,且必须为文件才为真
-L:检测是否是链接文件
-b:检测是否是设备文件
-c:检查是否是字符设备
-r:文档存在,且必须对其有读取权限才为真
-w:文档存在,且必须对其有写入权限才为真
-x:文档存在,且必须对其有执行权限才为真
-z:检查变量的值是否是空值
-n:检查变量是否是非空值
[root@server0 /]# [ -e /etc ]
[root@server0 /]# echo $?
[root@svr5 ~]# var1=“nb” ; var2=""
[root@svr5 ~]# [ -z “$var1” ] && echo “空值” || echo “非空值”
[root@svr5 ~]# [ ! -z $var1 ] //测试var1是否为非空
比较整数大小
-gt:大于
-ge:大于等于
-eq:等于
-lt:小于
-le:小于等于
-ne:不等于
[root@server0 /]# [ 1 -gt 1 ]
[root@server0 /]# echo $?
字符串比对
==:两个字符串一致为真
!=:两个字符串不一致为真
[root@server0 /]# [ redhat == CentOS ]
[root@server0 /]# echo $?
if条件语句
if 单分支
if [ 条件测试 ];then
命令序列xx
fi
if双分支处理
if [ 条件测试 ];then
命令序列xx
else
命令序列yy
fi
[root@server0 /]# vim /root/if01.sh
#!/bin/bash
if [ $1 == hi ];then
echo hello
else
echo hi
fi
[root@server0 /]# /root/if01.sh hi
if多分支处理
if [ 条件测试1 ];then
命令序列xx
命令序列bb
elif [条件测试2];then
命令序列yy
elif [条件测试3];then
命令序列aa
......
else
命令序列zz
fi
案例:
利用read 读取用户输入的成绩.
如果成绩 大于等于90,则输出 优秀
如果成绩 大于等于80,则输出 良好
如果成绩 大于等于70,则输出 一般
如果成绩 大于等于60,则输出 合格
以上条件均不满足,则输出 一首凉凉送给你!
[root@server0 /]# vim /root/if02.sh
#!/bin/bash
read -p '请输入您的成绩:' num
if [ $num -ge 90 ];then
echo 优秀
elif [ $num -ge 80 ];then
echo 良好
elif [ $num -ge 70 ];then
echo 一般
elif [ $num -ge 60 ];then
echo 合格
else
echo '一首凉凉送给你!'
fi
for循环
遍历/列表式循环,根据变量的不同取值,重复执行xx处理
for 变量名 in 值列表
do
重复执行的代码
done
[root@server0 ~]# vim /root/for01.sh
#!/bin/bash
for i in zhangsan lisi wangwu
do
useradd $i &> /dev/null
echo $i创建成功
done
[root@server0 ~]# vim /root/for02.sh
#!/bin/bash
for i in 1 2 3 4 5
do
echo 'I Love girl !!!'
done
案例:编写一个判断脚本
在 server0 上创建 /root/foo.sh 脚本
1)当运行/root/foo.sh redhat,输出为fedora
2)当运行/root/foo.sh fedora,输出为redhat
3)当没有任何参数或者参数不是 redhat 或者fedora时,
其错误输出产生以下信息: /root/foo.sh redhat|fedora
[root@server0 ~]# vim /root/foo.sh
#!/bin/bash
if [ $# -eq 0 ];then #判断用户是否输入参数
echo '/root/foo.sh redhat|fedora' >&2 #变成错误输出
exit 1 #程序退出状态码返回值为1
elif [ $1 == redhat ];then
echo fedora
elif [ $1 == fedora ];then
echo redhat
else
echo '/root/foo.sh redhat|fedora' >&2 #变成错误输出
exit 2 #程序退出返回值为2
fi
案例:编写一个批量添加用户脚本
在 server0 上创建 /root/batchusers 脚本
1)此脚本要求提供用户名列表文件作为参数
[root@server0 ~]# cat /root/userlist
duanwu
Zhongqiu
zhsan
lisi
dc
2)如果没有提供参数,此脚本应该给出提示
Usage: /root/batchusers,退出并返回相应值
3)如果提供一个不存在的文件,此脚本应该给出提
示 Input file not found,退出并返回相应值
4)新用户的登录Shell为 /bin/false,无需设置密码
[root@server0 ~]# vim /root/batchusers
#!/bin/bash
if [ $# -eq 0 ];then
echo 'Usage: /root/batchusers' >&2
exit 1
elif [ -f $1 ];then
for i in `cat $1`
do
useradd -s /bin/false $i &> /dev/null
echo $i 创建成功
done
else
Echo 'Input file not found' >&2
exit 2
Fi
while循环结构
while循环属于条件式的执行流程,会反复判断指定的测试条件,只要条件成立即执行固定的一组操作,直到条件变化为不成立为止。所以while循环的条件一般通过变量来进行控制,在循环体内对变量值做相应改变,以便在适当的时候退出,避免陷入死循环。
while 条件测试
do
命令序列
done
while :
do
命令序列
done
until循环结构
until 条件测试
do
命令序列
done
select循环结构
#!/bin/bash
PS3=”Your choice is:”
select choice in disk_partition filesystem cpu_load mem_util quit
do
case “$choice” in
disk_partition) fdisk -l;;
filesystem) df -h;;
cpu_load) uptime;;
mem_util) free -m;;
quit) break;;
*) echo “error”
exit
esac
done
case分支结构
case分支属于匹配执行的方式,它针对指定的变量预先设置一个可能的取值,判断该变量的实际取值是否与预设的某一个值相匹配,如果匹配上了,就执行相应的一组操作,如果没有任何值能够匹配,就执行预先设置的默认操作。
case 变量 in
模式1)
命令序列1 ;;
模式2)
命令序列2 ;;
.. ..
*)
默认命令序列
esac
函数
完成特定功能的特定片段(块),在shell中可以定义函数可以使用代码模块化,便于复用代码,必须先定义才可以使用.函数的返回值$?是函数最后一条命令的状态,也可以用return返回,最大是255。
函数名(){
函数要实现的功能代码
}
function 函数{
函数要实现的功能代码
}
函数的返回值:
#!/bin/bash
func() {
read -p “enter num:” num
echo $[2*$num]
}
result=`func`
echo “func return value:$result”
函数的参数:
#!/bin/bash
fun3(){
echo “$[$1*$2*$3]”
}
result=`fun3 $1 $2 $3`
echo “result is:$result”
#!/bin/bash
num=(1 2 3)
array() {
local factorial=1
for i in “$@” #$@表示所有参数
do
factorial=$[factorial * $i]
done
echo “$factorial”
}
array ${num[*]}
#!/bin/bash
num=(2 3 4)
array() {
local newarray=($*)
local i
for ((i=0;i<$#;i++))
do
newarry[$i]=$(( ${newarray[$i]} * 5))
done
echo “${newarry[*]}”
}
result=`array ${num[*]}`
echo ${result[*]}
#函数接收位置参数 $1 $2 ...$n
#函数接收数组变量$*或$@
#函数使用参数的个数$#
#函数将接收到的所有参数赋值给数组 newarry=($*)
#for i表示接收所有参数
Shell的内置命令:
contitinue:结束本次循环
break:结束以后的循环
exit:退出程序
:返回一个真值或站个位置
Shift 数值:左移位置参数
数组
普通数组:只能使用整数作为数组索引
关联数组:可以使用字符串作为数组索引
先声明Declare -a 变量名
一次赋一个值 array[0]=(dkkk) array[1]=(ssss) array[2]=(slkdkf)
一次赋多个值 array1=(tom jack alice) array2=(`cat /etc/password`)
查看单个元素 echo ${array[0]}
查看所有元素 echo ${array[@]}或echo ${array[*]}
统计数组元素的个数 echo ${#array[@]}
获取数组索引 echo ${ !array[@]}
显示变量的长度 echo ${#url}
通过数组索引遍历 echo ${array[@]:1}从数组下标1开始 echo ${array[@]:1:2}从数组下标1开始,访问后两个元素
统计性别:
法一:
awk ‘{print $2}’ sex.txt|sort|uniq -c
法二:
#!/bin/bash
#count sex
#v1.0 by jerome
declare -A sex
while read line
do
type=`echo $line|awk ‘{print $2}’`
let sex[$type]++
done < sex.txt
for i in ${!Sex[@]}
do
echo “$i:${sex[$i]}”
done
把要统计的对象作为数组的索引,while多用与文件
watch -n1 脚本或写个死循环 可以实时看状态
expect预期交换
expect可以为交互式过程(比如FTP、SSH等登录过程)自动输送预先准备的文本或指令,而无需人工干预。触发的依据是预期会出现的特征提示文本。
1)安装expect工具
2)在SSH登录过程中,如果是第一次连接到该目标主机,则首先会被要求接受密钥,然后才提示输入密码
[root@svr5 ~]# vim expect_ssh.sh
#!/bin/bash
expect << EOF
spawn ssh 192.168.4.5 #//创建交互式进程
expect "password:" { send "123456\r" } #//自动发送密码
expect "#" { send "touch /tmp.txt\r" } #//发送命令
expect "#" { send "exit\r" }
EOF
注意事项:
expect脚本的最后一行默认不执行
如果不希望ssh时出现yes/no的提示,远程时使用如下选项:
ssh -o StrictHostKeyChecking=no server0
expect实现非交互scp传输文件:
#!/bin/expect
Set ip [lindex $argv 0 ]
Set user [lindex $argv 1]
Set password centos
Set timeout 5
Spwan ssh $user@$ip
(spwan scp -r /etc/hosts $user@$ip:/tmp)
Expect{
“yes/no” {send “yes”\r;exp_continue}
“password:”{send “$password\r”};
}
Expect “#”
Send “useradd yangyang\r”
Send “pwd\r”
Send “exit\r”
Expect eof
expect批量公钥推送:
#!/bin/bash
>ip.txt //产生一个空白文件
Password=centos
Rpm -q expect &>/dev/null
If [ $? -ne 0 ];then
Yum -y install expect
Fi
If [ ! -f ~/.ssh/id_rsa ];then
Ssh-keygen -P “” -f ~/.ssh/id_rsa
Fi
for i in {1..254}
do
{
ip=192.168.122.$i
ping -c1 -w1 $ip &>/dev/null
if [ $? -eq 0 ];then
echo "$ip" >>ip.txt
/usr/bin/expect <<-EOF
Set timeout 10
Spwan ssh-copy-id $ip
Expect {
“yes/no” {send “yes”\r;exp_continue}
“password:”{send “$password\r”};
}
Expect eof
EOF
fi
}&
done
wait
echo "finish..."