SHELL

练习题网站

执行脚本方式

  1. +x权限
  2. bash
  3. source

source不开启子进程,其他两种会开启子进程

bash和source无需x权限

重定向输出信息

  1. 重定向正确输出 > /dev/null
  2. 重定向错误输出 2> /dev/null
  3. 重定向所有输出 &> /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

加减乘除、取模(取余数)

  1. 使用expr工具

例子:

expr 1 + 1

expr 2 - 1

expr 1 '*' 1 或 expr 1 \* 1

expr 1 / 1

expr 2 % 1
  1. echo $[1+1] 或echo $((1+1))

  2. 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--
  3. 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分支

  1. 单分支

    例子:

    #!/bin/bash
    if [ $UID -ne 0 ];then
    echo "你不是管理员"
    fi
    
  2. 双分支

    例子:

    #!/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
    
  3. 多分支

    例子:

    #!/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. 格式1:a()
  2. 格式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(逐行处理)

用法
  1. 前置指令 | sed 选项 条件 指令
  2. 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

可以实现精确搜索并输出,逐行处理

  1. 前置指令 | awk 选项 条件 指令

  2. 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
posted @ 2021-12-18 19:21  barry_zou  阅读(85)  评论(0)    收藏  举报