SHELL
执行脚本方式
- +x权限
- bash
- source
source不开启子进程,其他两种会开启子进程
bash和source无需x权限
重定向输出信息
- 重定向正确输出 > /dev/null
- 重定向错误输出 2> /dev/null
- 重定向所有输出 &> /dev/null
自定义变量
a=10
echo ${a}RMB //{}用于隔开变量与常量,避免混淆
export a=10 //发布为全局变量
export -n a //将全局变量恢复为局部变量
位置变量与预定义变量
$1 执行脚本时后面第1个位置参数
$2 执行脚本时后面第2个位置参数
$3 执行脚本时后面第3个位置参数
$* 执行脚本时后面所有位置参数
$# 执行脚本时后面位置参数的个数
$$ 随机进程号
$? 判断上一条指令是否执行成功,0是成功,非0是失败
例子
创建用户
vim useradd.sh
#!/bin/bash
read -p "请输入用户名:" username
stty -echo
read -p "请输入密码:" passwd
stty echo
useradd $username
echo "$passwd" | passwd --stdin $username
屏蔽回显 stty -echo
恢复回显 stty echo
加减乘除、取模(取余数)
- 使用expr工具
例子:
expr 1 + 1
expr 2 - 1
expr 1 '*' 1 或 expr 1 \* 1
expr 1 / 1
expr 2 % 1
-
echo $[1+1] 或echo $((1+1))
-
let 运算后无任何输出
- let c=$a+$b
- echo $c
- let a=a+2 或 let a+=2
- let a-=2
- let a*=2
- let a/=2
- let a%=2
对变量自增减
- let a++
- let a--
-
bc(支持小数运算)
- echo "1.1+2.2" | bc
- echo "scale=3;10/3" | bc //scale表示计算结果保留几位小数
- echo "1+1;2+2" //分号隔开,同时多个运算
条件测试
test a == a 或 [ a == a ]
echo $?
a=abc
b=xyz
[ $a == $b ] //使用变量判断两个变量的值是否相等
[ $a != $b ] //使用变量判断两个变量的值是否不相等
[ "$c" == $b ] //如果变量为空,有可能报错,加双引号可以避免
-z判断变量是否为空
[ -z $a ] //判断变量是否为空
echo $?
[ ! -z $a ] //判断变量是否非空
逻辑组合
&& //条件成功才执行后续指令
|| //条件失败才执行后续指令
当多个逻辑符号组合使用时
A && B //A、B任务都成功算成功
A || B //A、B任务有一个成功算成功
数字
-eq //是否相等
-ne //是否不等
-gt //是否大于
-ge //是否大于等于
-lt //是否小于
-le //是否小于等于
例子:
#!/bin/bash
x=`cat /etc/passwd | wc -l`
[ $x -gt 10 ]
echo "123"
文件
-e //判断文件是否存在,不关心类型
-f //判断文件是否存在
-d //判断目录是否存在
-r //判断当前用户对文件是否有读权限
-w //判断当前用户对文件是否有写权限
-x //判断当前用户对文件是否有执行权限
例子:
[ -d abc ] || mkdir abc //判断是否存在abc目录,如果失败就创建abc
if分支
-
单分支
例子:
#!/bin/bash if [ $UID -ne 0 ];then echo "你不是管理员" fi -
双分支
例子:
#!/bin/bash if [ $UID -ne 0 ];then echo "你不是管理员" else echo "你是管理员" fi判断一个地址通不通:
#! /bin/bash ping -c 3 -i 0.2 -W 1 192.168.2.5 &> /dev/null #-c指定ping次数,-i指定每次ping的时间,-W表示当ping不通时多久返回结果 if [ $? -eq 0 ];then echo "通了!" else echo "不通!" fi -
多分支
例子:
#!/bin/bash read -p "猜数字游戏,请猜一个数字:" x if [ $x -eq 5 ];then echo "猜对了!" elif [ $x -gt 5 ];then echo "猜大了!" else echo "猜小了" fi
循环
for循环
例子:
#!/bin/bash
for i in {1..10}
do
echo $i
done
例子2:
#!/bin/bash
#ping测试,如果通x+1,不通则y+1
x=0
y=0
for i in {1..100}
do
ping -c 1 -i 0.2 -W 1 192.168.1.$i &> /dev/null
if [ $? -eq 0 ];then
echo "1.$i通了!"
let x++
else
echo "1.$i不通!"
let y++
fi
done
echo "通了$x台,不通$y台"
while循环
while :或while true表示永远为真
例子:
#!/bin/bash
n=10
while [ $n -ge 5 ]
do
echo abc
sleep 0.1
let n--
done
循环的控制
exit 终止循环,停止脚本
break 终止循环,继续循环外任务
continue 跳出当前循环,继续下一次循环
例子:
#!/bin/bash
x=0
while :
do
read -p "求和,请输入一个数字,输入0结束:" y
[ -z $y ] && continue
let x+=y
[ $y -eq 0 ] && break
done
echo "和为$x"
case分支,功能类似if,编写时语句比if精简
例子:
#!/bin/bash
read -p "需要进行的操作(t/m/r):" x
read -p "文件或目录名:" y
case $x in
t|T|touch) # |表示或者
touch $y;;
m|M|mkdir)
mkdir $y;;
r|R|rm)
rm -rf $y;;
*)
echo "请输入t|m|r"
esac
对比if分支:
#!/bin/bash
read -p "需要进行的操作(t/m/r):" x
read -p "文件或目录名:" y
if [ $x == t ];then
touch $y
elif [ $x == m ];then
mkdir $y
elif [ $x == r ];then
rm -rf $y
else
echo "请输入t|m|r"
fi
可以看到,用if分支要稍微麻烦一些,并且当x需要匹配多个条件时,case分支只需要一个|即可做到,
if需要写多个条件
例子2(安装或卸载nginx):
#!/bin/bash
echo -e "\033[32m此脚本用于安装或卸载nginx\033[0m"
read -p "请选择安装还是卸载nginx(1.安装|2.卸载):" x
case $x in
1)
wget http://nginx.org/download/nginx-1.20.2.tar.gz
tar -xf nginx-1.20.2.tar.gz
yum -y install gcc make pcre-devel openssl-devel &> /dev/null
[ $? -ne 0 ] && echo -e "\033[32m依赖包未安装成功\033[0m"
cd nginx-1.20.2/
./configure &> /dev/null && make &> /dev/null && make install &> /dev/null
[ $? -eq 0 ] && echo -e "\033[32mnginx已安装\033[0m"
/usr/local/nginx/sbin/nginx
netstat -ntulp | grep -q nginx
[ $? -eq 0 ] && echo -e "\033[32mnginx已启动\033[0m";;
2)
/usr/local/nginx/sbin/nginx -s stop
netstat -ntulp | grep -q nginx
[ $? -ne 0 ] && echo -e "\033[32mnginx已停止\033[0m"
#find / -name nginx -exec rm -rf {} \;
rm -rf /usr/local/nginx
[ $? -eq 0 ] && echo -e "\033[32mnginx已卸载\033[0m";;
*)
echo "请输入正确的选项(1.安装|2.卸载)"
esac
函数
- 格式1:a()
- 格式2:function a
例子:
#!/bin/bash
a(){
echo -e "\033[$1m$2\033[0m" #函数中的位置变量在脚本内调用,不在脚本外
}
a 31 红色 #调用函数
a 32 绿色
a 33 黄色
a 34 蓝色
a 35 紫色
例子2(利用函数改造nginx脚本):
#!/bin/bash
#定义颜色函数
color(){
echo -e "\033[32m$1\033[0m"
}
color 此脚本用于安装或卸载nginx
read -p "请选择安装还是卸载nginx(1.安装|2.卸载):" x
case $x in
1)
wget http://nginx.org/download/nginx-1.20.2.tar.gz
tar -xf nginx-1.20.2.tar.gz
yum -y install gcc make pcre-devel openssl-devel &> /dev/null
[ $? -ne 0 ] && color 依赖包未安装成功
cd nginx-1.20.2/
./configure &> /dev/null && make &> /dev/null && make install &> /dev/null
[ $? -eq 0 ] && color nginx已安装
/usr/local/nginx/sbin/nginx
netstat -ntulp | grep -q nginx
[ $? -eq 0 ] && color nginx服务已启动;;
2)
/usr/local/nginx/sbin/nginx -s stop
netstat -ntulp | grep -q nginx
[ $? -ne 0 ] && color nginx服务已停止
#find / -name nginx -exec rm -rf {} \;
rm -rf /usr/local/nginx
[ $? -eq 0 ] && color nginx已卸载;;
*)
echo "请输入正确的选项(1.安装|2.卸载)"
esac
字符串截取
格式: ${变量名称:截取位置:截取长度}
例子:
[root@k8s-master ~]# a=abcde
[root@k8s-master ~]# echo ${a:2:3} //从第3位截取,截取3位字符
cde
[root@k8s-master ~]# echo ${a:0:4}
abcd
[root@k8s-master ~]# echo ${a::4}
abcd
[root@k8s-master ~]# echo ${a::-2}
abc
[root@k8s-master ~]# echo ${a:1:-2} //从第2位截取到倒数第3位
bc
RANDOM
例子(创建随机密码):
#!/bin/bash
string=abcdefghijklmnopqrstuvwxyz
read -p "您想生成几位密码:" x
for i in `seq 1 $x`
do
n=${string:RANDOM%26:1} #每次随机截取一位
passwd=$passwd$n
done
echo $passwd
seq命令
seq 用于生成从一个数到另一个数之间的所有整数。
例子:
[root@k8s-master ~]# seq 1 5
1
2
3
4
5
字符串替换
格式 ${变量名称/旧值/新值}
例子:
[root@k8s-master ~]# a=11223344
[root@k8s-master ~]# echo ${a/1/6} //将首个1替换成6
61223344
[root@k8s-master ~]# echo ${a//1/6} //将所有的1替换成6
66223344
[root@k8s-master ~]# echo ${a/1/} //将首个1替换成空,相当于删除
1223344
字符串删除
掐头:${变量名称#需要删除的内容}
去尾:${变量名称%需要删除的内容}
例子:
[root@k8s-master ~]# a=abcdefg
[root@k8s-master ~]# echo ${a#ab} //从头开始删除,删到b
cdefg
[root@k8s-master ~]# echo ${a#*f} //从头开始删除,删到f
g
[root@k8s-master ~]# echo ${a%b*} //从后开始删除,删到b
a
例子2(批量修改扩展名):
touch zzy{1..100}.txt //创建素材
#!/bin/bash
for i in `ls *.txt`
do
n=${i%.*} #去尾
mv $i $n.md
done
变量定义初值
防止变量值为空时无法调用,相当于设置一个初始值,默认值。
例子:
[root@k8s-master ~]# a=
[root@k8s-master ~]# echo $a
[root@k8s-master ~]# echo ${a:-123}
123
正则表达式
基本正则列表
| 正则符号 | 描述 |
|---|---|
| ^ | 匹配行首 |
| $ | 匹配行尾 |
| [] | 集合,匹配集合中的任意单个字符 |
| [^] | 对集合取反 |
| . | 匹配任意单个字符 |
| * | 匹配前一个字符任意次数[*不允许单独使用] |
| \ | 匹配前一个字符n到m次 |
| \ | 匹配前一个字符n次 |
| \ | 匹配前一个字符n次及以上 |
| \ | 保留 |
扩展正则列表
| 正则符号 | 描述 |
|---|---|
| + | 最少匹配一次 |
| ? | 最多匹配一次 |
| 匹配n到m次 | |
| () | 组合为整体,保留 |
| | | 或者 |
| \b | 单词边界 |
例子:
准备文件:head -5 /etc/passwd > user
基本正则:
grep ^root user //匹配以root开头的行
grep bash$ user //匹配以bash结尾的行
grep ^$ user //匹配空行
grep -v ^$ user //匹配非空行
grep "[root]" user //匹配含有集合中rot的所有字符
grep "[^root]" user //匹配不含集合中rot的所有字符(显示除了rot以外的所有内容)
grep "[a-z0-9]" user //匹配所有字符和数字
grep ro*t user //匹配以r开头,t结尾,中间任意个o的行,o可以为0
grep r..t user //匹配以r开头,t结尾,中间2个o的行
grep ".*" user //匹配所有内容
grep "ro\{1,2\}t" user //匹配以r开头,t结尾,有1-2个o的行
grep "ro\{2\}t" user //匹配以r开头,t结尾,有2个o的行
grep "ro\{1,\}t" user //匹配以r开头,t结尾,有一个及以上o的行
扩展正则:
egrep "ro{2,6}t" user //匹配以r开头,t结尾,有2-6个o的行
egrep "ro+t" user //匹配以r开头,t结尾,有1个及以上o的行
egrep "ro?t" user //匹配以r开头,t结尾,有0-1个o的行
egrep "(0:){2}" user //匹配连续2次0:的行
egrep "\bthe\b" the.txt //精准匹配含有the的行(给the两边设置边界,精准匹配)
egrep "\<the\>" the.txt //同上
sed(逐行处理)
用法
- 前置指令 | sed 选项 条件 指令
- sed 选项 条件 指令 被处理文档
选项
-n 屏蔽默认输出
-r 支持扩展正则
-i 修改源文件
指令
p 输出
d 删除
s 替换
条件 行号 /字符串/
例子:
sed -n 'p' user //输出user表内容
sed -n '2p' user //输出第二行内容
sed -n '2!p' user //输出第二行以外的内容
sed -n '2p;4p' user //输出第二行和第四行
sed -n '2,4p' user //输出2-4行内容
sed -n '2,+1p' user //输出2行以及之后的一行内容
sed -n '/root/p' user //输出含有root的行
sed -nr '/^root|^bin/p' user //输出以root或者bin开头的行
sed -n '=' user //输出行号
sed -n '$=' user //输出最后一行行号
sed -n '$p' user //输出最后一行内容
以上操作,将p换成d就是删除操作
sed '/root/!d' user //删除不含root的行
sed '/^$/d' user //删除空行
替换操作(重点)
素材:
cat > test.txt <<EOF
2017 2011 2018
2017 2017 2024
2017 2017 2017
EOF
sed 's/2017/6666/' test.txt //替换所有行的第一个2017
sed '2s/2017/6666/' test.txt //替换第二行的第一个2017
sed '/2024/s/2017/6666/' test.txt //替换含有2024的行的第一个2017
sed '/2017$/s/2017/6666/' test.txt //替换2017结尾的行的第一个2017
sed 's/2017/6666/2' test.txt //替换所有行的第2个2017
sed 's/2017/6666/g' test.txt //替换所有行的所有2017
sed -r 's/^(.+)$/#\1/' test.txt //将所有行加上#注释
sed '/^SELINUX/s/enforcing/disabled/' /etc/selinux/config
//替换以SELINUX开头的行的第一个enforcing
如果遇到需要替换的内容有/,那么s///可以替换为s%%%(%可以是任意符号)
虽然可以使用转义符,但太麻烦 s/\/bin\/bash/\/sbin\/nologin/
sed 's%/bin/bash%/sbin/nologin%' user//替换所有行的第一个/bin/bash
例子:
编写脚本,找到使用bash的账户名,并按照''用户名-->密码''的格式存储在一个文件中
#!/bin/bash
users=`sed -n '/bash$/s/:.*//p' /etc/passwd` #找bash结尾的行并删除第一个:以后的内容
for user in $users
do
passwd=`grep "$user" /etc/shadow`
passwd=${passwd#*:} #掐头
passwd=${passwd%%:*} #去尾
echo "$user-->$passwd" >> user.txt
done
扩展:
\w 匹配数字、字母、下滑线
\s 匹配空格和tab
例:
egrep "roo\w" user
egrep "roo\s" user
sed追加指令
a 行下追加
i 行上追加
c 替换整行
例:
sed 'a 666' user //所有行下追加666
sed '1a 666' user //第一行下追加
sed '$a 666' user //最后一行下追加
sed '/^root/a 666' user //在以root开头的行下追加
sed 'i 666' user //所有行上追加
sed '2i 666' user //第二行上追加
sed '$i 666' user //最后一行上追加
sed 'c 666' user //所有行内容替换为666
sed '2c 666' user //第二行内容替换
awk
可以实现精确搜索并输出,逐行处理
-
前置指令 | awk 选项 条件 指令
-
awk 选项 条件 指令 被处理文档
选项 -F 定义分隔符
指令 print printf
- print 和 printf 的区别在于printf不换行
条件 /字符串/
内置变量
$1 第一列
$2 第二列
...
$0 所有列
NR 行号
NF 列号
例:
准备素材:
vim abc.txt
hello the world
welcome to beijing
[root@k8s-master ~]# awk '{print}' abc.txt //输出所有
hello the world
welcome to beijing
[root@k8s-master ~]# awk '/the/{print}' abc.txt //输出带the的行
hello the world
[root@k8s-master ~]# awk '/the/{print $1}' abc.txt //输出带the的行的第一列
hello
[root@k8s-master ~]# awk '/the/{print $0,$1}' abc.txt //输出带the的行的所有列,第一列
hello the world hello
[root@k8s-master ~]# awk '{print NR}' abc.txt //输出所有行行号
1
2
[root@k8s-master ~]# awk '{print NF}' abc.txt //输出所有列列号
3
3
[root@k8s-master ~]# awk '/^root/{print NR}' user //输出以root开头的行的行号
1
[root@k8s-master ~]# awk '/^root/{print NF}' user //输出以root开头的行的列号
1
[root@k8s-master ~]# awk '/^root/{print NR,$0}' user //输出以root开头的行的行号,所有列内容
1 root:x:0:0:root:/root:/bin/bash
[root@k8s-master ~]# awk -F : '/^root/{print $1}' user //输出以root开头的行的第一列,以:作为分隔符
root
[root@k8s-master ~]# awk -F : '{print $1"的解释器是"$7}' user //以双引号作为分隔符输出常量
root的解释器是/bin/bash
bin的解释器是/sbin/nologin
[root@k8s-master ~]# df -h | awk '/\/$/{print "根分区的总容量为"$2}' //输出以“/”作为结尾列的行的第二列
[root@k8s-master ~]# ifconfig ens33 | awk '/RX p/{print "ens33 网卡接收的流量是"$6$7}' | sed 's/(//' | sed 's/)//'
ens33 网卡接收的流量是1.5MiB
awk的条件:
- ~包含
- !~不包含
- ==
- !=
- >
- >=
- <
- <=
- && 并且
- || 或者
- awk还能做运算
awk 'NR==2{print}' user //输出第二行
awk 'NR!=2{print}' user //输出第二行以外的行
awk 'NR>3{print}' user //输出行号大于3的行
awk -F : '$5~/adm/{print}' user //输出第5列包含adm的行
awk -F : '$5!~/adm/{print}' user //输出第5列不包含adm的行
awk -F : '$3>=1000{print}' /etc/passwd //输出第三列大于等于1000的行(普通用户uid>=1000)
awk -F : '$3>=1&&$3<=5{print}' user //输出第三列>=1并且<=5的行
awk -F : 'NR>=3&&NR<=5{print}' user //输出第3到第5行
awk -F : '$3>=1||$3<=5{print}' user //输出第1行和第5行
awk -F : 'NR%2==0{print NR,$0}' user //输出偶数行
awk处理时机,处理额外任务
-
BEGIN任务 读取文档之前执行一次
-
逐行任务 读取文档时执行n次
-
END任务 读取文档之后执行一次
BEGIN{任务1}{任务2}END{任务3}
例:
awk -F: 'BEGIN{print "users:"}{print $1}END{print "一共有"NR"行"}' user
[root@k8s-master ~]# awk -F: 'BEGIN{print "USER\tUID\tHOME"}{print $1"\t"$3"\t"$6}END{print "总计"NR"行"}' user
USER UID HOME
root 0 /root
bin 1 /bin
daemon 2 /sbin
adm 3 /var/adm
lp 4 /var/spool/lpd
总计5行
使用数组+for循环实现高级搜索
数组 相当于可以存储多个值的特殊变量
数组名[下标]=下标对应的值
for(变量名 in 数组名){print 变量名} //这个格式可以查看数组的所有下标
例:
统计日志
awk '{ip[$1]++}END{for(i in ip){print i"\t"ip[i]}}' /var/log/httpd/access_log
#排序
awk '{ip[$1]++}END{for(i in ip){print i"\t"ip[i]}}' /var/log/httpd/access_log | sort -nr -k 2
sort命令选项
-n 以数字形式排序
-r 降序排序
-k 指定参照第几列排序
#查找输错密码的ip以及次数
awk '/Failed password for root/{ip[$11]++}END{for(i in ip){print i"\t"ip[i]}}' /var/log/secure
练习:监控系统参数
#!/bin/bash
while :
do
free -h | awk '/^Mem/{print "剩余内存为"$4}'
df -h | awk '/\/$/{print "根分区总容量为"$2",""已使用"$5",""剩余容量为"$4}'
awk 'END{print "总用户有"NR"个"}' /etc/passwd
who | awk 'END{print "当前登陆人数为"NR"个"}'
uptime | awk '{print "CPU15分钟平均负载为"$NF}'
sleep 1
clear
done

浙公网安备 33010602011771号