Skip to content

Shell概述

shell
[root@test ~]# vim user.sh
#!/bin/bash(环境声明)
#注释信息
可执行代码…

如何写好一个shell脚本

  • 明确任务需求
  • 按需求整理好每一个步骤,先做什么,后做什么
  • 运行脚本,并根据运行结果排除错误
  • 优化脚本并达到最终效果

编写脚本

  • 编写第一个脚本
shell
[root@localhost ~]# vim hello.sh
#!/bin/bash
#hello word
echo hello word

#赋予执行权限
[root@localhost ~]# chmod u+x hello.sh

#执行脚本
[root@localhost ~]# /root/hello.sh 
hello word
  • 编写创建用户脚本

    shell
    [root@localhost ~]# vim user.sh
    #!/bin/bash
    useradd abc  
    passwd abc
    
    [root@localhost ~]# chmod u+x user.sh 
    
    #非交互
    [root@localhost ~]# vim user.sh
    #!/bin/bash
    useradd yyyy
    echo 1 | passwd --stdin yyyy
    
    [root@localhost ~]# ./user.sh 
    更改用户 yyyy 的密码
    passwd:所有的身份验证令牌已经成功更新。
    • 编写批量查看脚本

    shell
    #查看系统版本信息,查看系统内核信息,查看系统内存信息,查看系统网卡信息,查看当前主机名
    [root@localhost ~]# vim info.sh
    cat /etc/redhat-release
    uname -r
    free -h
    ifconfig ens32
    hostname
    
    #赋予执行权限
    [root@localhost ~]# chmod u+x info.sh 
    
    #执行脚本
    [root@localhost ~]# ./info.sh 
    CentOS Linux release 7.6.1810 (Core) 
    3.10.0-957.el7.x86_64
                  total        used        free      shared  buff/cache   available
    Mem:           972M        480M        147M         16M        344M        260M
    Swap:          2.0G        239M        1.8G
    ens32: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
            inet 192.168.0.100  netmask 255.255.255.0  broadcast 192.168.0.255
            inet6 fe80::8903:cb8:127b:dbc8  prefixlen 64  scopeid 0x20<link>
            ether 00:0c:29:a0:e8:12  txqueuelen 1000  (Ethernet)
            RX packets 230110  bytes 325471733 (310.3 MiB)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 20635  bytes 1385581 (1.3 MiB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
    
    localhost.localdomain
    • 编写配置本地yum源脚本

    shell
    #编写搭建本地yum仓库脚本【版1,丢人版】
    [root@localhost ~]# vim yum.sh 
    #!/bin/bash
    mkdir /mnt/centos
    mount /dev/cdrom /mnt/centos
    echo '/dev/cdrom /mnt/centos iso9660 defaults 0 0' >> /etc/fstab
    rm -rf /etc/yum.repos.d/*
    touch  /etc/yum.repos.d/local.repo
    echo "[local]" > /etc/yum.repos.d/local.repo
    echo "name=local_centos" >> /etc/yum.repos.d/local.repo
    echo "baseurl=file:///mnt/centos" >> /etc/yum.repos.d/local.repo
    echo "enabled=1" >> /etc/yum.repos.d/local.repo
    echo "gpgcheck=0" >> /etc/yum.repos.d/local.repo
    
    #编写搭建本地yum仓库脚本【版2,正常版】
    [root@localhost ~]# vim yum.sh 
    #!/bin/bash
    mkdir /mnt/centos
    mount /dev/cdrom /mnt/centos
    echo '/dev/cdrom /mnt/centos iso9660 defaults 0 0' >> /etc/fstab
    mount -a
    rm -rf /etc/yum.repos.d/*
    echo "[local] 
    name=local_centos 
    baseurl=file:///mnt/centos 
    enabled=1 
    gpgcheck=0" > /etc/yum.repos.d/local.repo
    
    #编写搭建本地yum仓库脚本【升级版】
    [root@localhost ~]# vim yum.sh 
    #!/bin/bash
    echo "正在配置本地yum仓库..."
    mkdir /mnt/centos
    mount /dev/cdrom /mnt/centos &> /dev/null
    echo '/dev/cdrom /mnt/centos iso9660 defaults 0 0' >> /etc/fstab
    mount -a
    rm -rf /etc/yum.repos.d/*
    echo "[local] 
    name=local_centos 
    baseurl=file:///mnt/centos 
    enabled=1 
    gpgcheck=0" > /etc/yum.repos.d/local.repo
    echo "本地yum仓库配置以完成..."
    yum clean all &> /dev/null
    yum repolist | tail -1
    
    
    #执行脚本
    [root@localhost ~]# ./yum.sh
    正在配置本地yum仓库...
    本地yum仓库配置以完成...
    repolist: 4,021

    脚本的执行方式

    • 执行一个脚本的方法有很多种
    • 方法一:赋予脚本执行权限后,可用绝对路径或者当前路径执行
    • 方法二:调用解释器执行脚本文件
    shell
    #绝对路径执行脚本
    [root@localhost ~]# /root/hello.sh 
    
    #相对路径执行脚本
    [root@localhost ~]# ./hello.sh 
    
    #去除执行权限
    [root@localhost ~]# chmod u-x hello.sh 
    
    #执行脚本
    [root@localhost ~]# /root/hello.sh
    -bash: /root/hello.sh: 权限不够
    
    [root@localhost ~]# ./hello.sh
    -bash: ./hello.sh: 权限不够
    
    #调用解释器执行脚本
    [root@localhost ~]# bash hello.sh
    hello word
    
    [root@localhost ~]# cat /etc/shells 
    /bin/sh
    /bin/bash
    /usr/bin/sh
    /usr/bin/bash
    /bin/tcsh
    /bin/csh
    [root@localhost ~]# sh hello.sh
    hello word
    [root@localhost ~]# tcsh hello.sh
    hello word
    [root@localhost ~]# csh hello.sh
    hello word

    常用特殊符号补充

  • “ “ #双引号,引用整体

  • ‘ ’ #单引号,引用整体并取消所有特殊字符含义

  • $[] #四则运算(+ - * / % 取余数)

  • $() #将命令的输出结果作为参数

  • #反撇 `` 将命令的输出结果作为参数

shell
#引用整体,不屏蔽特殊符号的功能
[root@localhost ~]# echo "$PATH"
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin

#引用整体,屏蔽特殊符号的功能
[root@localhost ~]# echo '$PATH'
$PATH

#没有特殊符号单引双引都可以
[root@localhost ~]# echo "xxoo"
xxoo
[root@localhost ~]# echo 'xxoo'
xxoo

#四则运算
[root@localhost ~]# echo $[1+1]
2
[root@localhost ~]# echo $[1+5]
6
[root@localhost ~]# echo $[10-5]
5
[root@localhost ~]# echo $[10*5]
50
[root@localhost ~]# echo $[10/5]
2
[root@localhost ~]# echo $[1+3+4+5+7]
20
[root@localhost ~]# echo $[10/3]
3
[root@localhost ~]# echo $[10%3]
1

#$()取命令结果作为参数
root@localhost ~]# touch $(date +%F)-abc.txt
2021-05-09-abc.txt 

#``取命令结果作为参数
[root@localhost ~]# touch `date +%F`-xxoo.txt
2021-05-09-xxoo.txt

变量

  • 以固定的名称存放可能变化的值,提高脚本的灵活度来适应多变的环境
  • 定义变量:变量名=变量值,如:a1=abc(等号两边不要有空格)
  • 定义变量注意事项:
    • 变量名由字母/数字/下划线组成,区分大小写,不能以数字开头,不要使用命令和特殊符号
  • 若指定的变量名已经存在,相当于为此变量重新赋值
  • 取消变量:unset 变量名
shell
root@localhost ~]# xx=haha

[root@localhost ~]# echo $xx
haha

[root@localhost ~]# xx=abcd
[root@localhost ~]# echo $xx
abcd

[root@localhost ~]# xx=5
[root@localhost ~]# echo $xx
5
[root@localhost ~]# echo $[xx+5]
10

#通过变量定义用户名
[root@localhost ~]# vim user.sh 
#!/bin/bash
user=wangxin
useradd $user   
echo 1 | passwd --stdin $user

[root@localhost ~]# ./user.sh 
更改用户 wangxin 的密码
passwd:所有的身份验证令牌已经成功更新。

[root@localhost ~]# vim user.sh 
#!/bin/bash
user=sdd
useradd $user
echo 1 | passwd --stdin $user

[root@localhost ~]# ./user.sh 
更改用户 sdd 的密码
passwd:所有的身份验证令牌已经成功更新。

[root@localhost ~]# vim user.sh 
#!/bin/bash
user=panghu
useradd $user
echo "用户$user创建成功"
echo 1 | passwd --stdin $user &> /dev/null
echo "用户$user密码设置成功"

[root@localhost ~]# ./user.sh 
用户panghu创建成功
用户panghu密码设置成功

read标准输入取值

  • read 读取用户在键盘上输入的内容,并把内容存放在变量里,可以降低脚本的使用难度
  • 命令格式:read -p “提示信息” 变量名
shell
[root@localhost ~]# vim user.sh 
#!/bin/bash
read -p '请输入用户名:' user
useradd $user 
echo "用户$user创建成功"
read -p '请设置用户密码:' pass
echo $pass | passwd --stdin $user &> /dev/null
echo "用户$user密码设置成功"

[root@localhost ~]# ./user.sh 
请输入用户名:wuhan
用户wuhan创建成功
请设置用户密码:1
用户wuhan密码设置成功

[root@localhost ~]# ./user.sh 
请输入用户名:liangjing
用户liangjing创建成功
请设置用户密码:xxoo
用户liangjing密码设置成功

变量种类

  • 环境变量:变量名一般都大写,用来设置用户/系统环境

  • 位置变量:bash内置,存储执行脚本时提供的命令参数

  • 预定义变量:bash内置,可直接调用的特殊值,不能直接修改

  • 自定义变量:用户自定义

  • env 命令查看系统所有环境变量

  • set 命令查看系统所有变量,包括用户自定义变量

  • 环境变量

shell
[root@localhost etc]# env
MAIL=/var/spool/mail/root
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
PWD=/root
LANG=zh_CN.UTF-8
SELINUX_LEVEL_REQUESTED=
HISTCONTROL=ignoredups
SHLVL=1
HOME=/root
LOGNAME=root
XDG_DATA_DIRS=/root/.local/share/flatpak/exports/share:/var/lib/flatpak/exports/share:/usr/local/share:/usr/share
SSH_CONNECTION=192.168.0.1 51791 192.168.0.100 22
LESSOPEN=||/usr/bin/lesspipe.sh %s
XDG_RUNTIME_DIR=/run/user/0
DISPLAY=localhost:10.0
_=/usr/bin/env
OLDPWD=/root

#获取变量值
[root@localhost etc]# echo $SHELL
/bin/bash

[root@localhost ~]# echo $PWD
/root

#查看系统所有变量
[root@localhost ~]# set
[root@localhost ~]# set | grep $a
  • 位置变量

    $0 #脚本名称

    $1 #第一个参数

    $2 #第二个参数

    $3 #第三个参数

    $4 #第四个参数

    $n… #第n个参数

  • 预定义变量

    $0 #代表脚本本身

    $* #显示所有参数内容

    $# #显示有多少个参数

    $? #显示上一条命令的执行结果(0代表正确,非0代表错误)

    $$ #显示脚本进程号(PID)

shell
[root@localhost ~]# vim test.sh 
#!/bin/bash
echo $0
echo $1
echo $2
echo $3
echo $*
echo $#
echo $$
echo $?

#赋予执行权限,执行脚本
[root@localhost ~]# ./test.sh xx oo dd
./test.sh  $0
xx         $1
oo         $2
dd         $3
xx oo dd   $*
3          $#
15594      $$
0          $?

判断文件状态

  • -e #判断文档(文件/目录)是否存在,存在为真
  • -d #判断目录是否存在,存在为真
  • -f #判断文件是否存在,存在为真
  • -r #可读为真
  • -w #可写为真
  • -x #可执行为真
shell
#判断文档是否存在
[root@localhost ~]# [ -e /etc/ ]
[root@localhost ~]# echo $?
0 #为真

#判断目录是否存在
[root@localhost ~]# [ -d /opt ]
[root@localhost ~]# echo $?
0

[root@localhost ~]# [ -d /etc/passwd ]
[root@localhost ~]# echo $?
1

[root@localhost ~]# [ -f /etc/passwd ]
[root@localhost ~]# echo $?
0

#判断是否可读(以当前用户身份判断)
[root@localhost ~]# [ -r /etc/passwd ]
[root@localhost ~]# echo $?
0

#判断是否可写
[root@localhost ~]# [ -w /etc/passwd ]
[root@localhost ~]# echo $?
0

#判断是否可执行
[root@localhost ~]# [ -x /etc/passwd ]
[root@localhost ~]# echo $?
1
[root@localhost ~]# ll /etc/passwd
-rw-r--r--. 1 root root 3294 5月   9 16:43 /etc/passwd

整数比较

  • -gt #大于
  • -ge #大于等于
  • -eq #等于
  • -lt #小于
  • -le #小于等于
  • -ne #不等于
shell
[root@localhost ~]# [ 1 -gt 1 ]
[root@localhost ~]# echo $?
1

[root@localhost ~]# [ 1 -eq 1 ]
[root@localhost ~]# echo $?
0

[root@localhost ~]# [ 1 -ge 1 ]
[root@localhost ~]# echo $?
0

[root@localhost ~]# [ 1 -ge 2 ]
[root@localhost ~]# echo $?
1

[root@localhost ~]# [ 1 -lt 2 ]
[root@localhost ~]# echo $?
0

[root@localhost ~]# [ 1 -le 2 ]
[root@localhost ~]# echo $?
0

[root@localhost ~]# [ 1 -le 10 ]
[root@localhost ~]# echo $?
0
[root@localhost ~]# [ 10 -le 10 ]
[root@localhost ~]# echo $?
0

[root@localhost ~]# [ 1 -ne 2 ]
[root@localhost ~]# echo $?
0

[root@localhost ~]# [ 2 -ne 2 ]
[root@localhost ~]# echo $?
1

字符串对比

  • == #相等

  • != #不相等

shell
[root@localhost ~]# [ root == xxoo ]
[root@localhost ~]# echo $?
1

[root@localhost ~]# [ root == $USER ]
[root@localhost ~]# echo $?
0

[root@localhost ~]# [ $USER == root ]
[root@localhost ~]# echo $?
0

[root@localhost ~]# [ abc == bcd ]
[root@localhost ~]# echo $?
1

[root@localhost ~]# [ abc != bcd ]
[root@localhost ~]# echo $?
0

[root@localhost ~]# [ $USER != root ]
[root@localhost ~]# echo $?
1

常用数值运算方式

  • $[] #四则运算(+ - * / % 取余数)
  • $(()) #数值运算工具
  • expr #数值运算工具
  • let #数值运算工具
shell
[root@localhost ~]# echo $[10+5]
15
[root@localhost ~]# echo $[10-5]
5
[root@localhost ~]# echo $[10*5]
50
[root@localhost ~]# echo $[10/5]
2
[root@localhost ~]# echo $[10%3]
1

#$(())做数值运算
[root@localhost ~]# echo $((10+5))
15
[root@localhost ~]# echo $((10-5))
5
[root@localhost ~]# echo $((10*5))
50
[root@localhost ~]# echo $((10/5))
2
[root@localhost ~]# echo $((10%3))
1

#expr做数值运算
[root@localhost ~]# echo expr 2+3
expr 2+3
[root@localhost ~]# echo `expr 2+3`
2+3

#要求每一部分都要有空格
[root@localhost ~]# echo `expr 2 + 3`
5
[root@localhost ~]# echo `expr 10 - 3`
7

#当进行乘法运算时,需要实用“\”转义掉*的特殊功能
[root@localhost ~]# echo `expr 10 * 3`
expr: 语法错误

[root@localhost ~]# echo `expr 10 \* 3`
30
[root@localhost ~]# echo `expr 10 / 3`
3

#let做数值运算
[root@localhost ~]# let 1+1

#需要将运算的结果赋予一个变量存储
[root@localhost ~]# let x=1+1
[root@localhost ~]# echo $x
2
[root@localhost ~]# let a=10+5
[root@localhost ~]# echo $a
15

#通过变量的值进行运算
[root@localhost ~]# let b=a+x
[root@localhost ~]# echo $b
17

#let简写表达式	     #let完整表达式
let i++      		let i=i+1
[root@localhost ~]# i=10
[root@localhost ~]# let i++
[root@localhost ~]# echo $i
11

let i--        		let i=i-1
[root@localhost ~]# let i--
[root@localhost ~]# echo $i
10

let i+=2    		let i=i+2
[root@localhost ~]# let i+=2
[root@localhost ~]# echo $i
12

let i-=2     		let i=i-2
[root@localhost ~]# let i-=2
[root@localhost ~]# echo $i
10

let i*=2			let i=i*2
[root@localhost ~]# let i*=2
[root@localhost ~]# echo $i
20

let i/=2			let i=i/2
[root@localhost ~]# let i/=2
[root@localhost ~]# echo $i
10

let i%=2			let i=1%2
[root@localhost ~]# let i%=3
[root@localhost ~]# echo $i
1

字符串判断

  • -z #字符串的值为空为真
  • -n #字符串的值非空为真(相当于 ! -z)
shell
#判断文件为空为真
[root@localhost ~]# [-z /etc/passwd]
bash: [-z: 未找到命令...

#判断时每一部分要有空格
[root@localhost ~]# [ -z /etc/passwd ]
[root@localhost ~]# echo $?
1

#判断文件非空为真
[root@localhost ~]# [ -n /etc/passwd ]
[root@localhost ~]# echo $?
0

[root@localhost ~]# [ -z $i ]
[root@localhost ~]# echo $?
1

[root@localhost ~]# [ -n $i ]
[root@localhost ~]# echo $?
0

[root@localhost ~]# [ ! -z $i ]
[root@localhost ~]# echo $?
0

条件判断结构

  • 当条件满足时执行什么操作,当条件不满足时执行什么操作
  • && #逻辑与(并且)
  • || #逻辑或(或者)
  • ; #条件之间没有逻辑关系
shell
A && B  #当A命令执行成功后才会执行B,如果A执行失败则B不执行
#判断存在且是文件执行拷贝操作(并且关系)
[root@localhost ~]# [ -f /etc/passwd ] && cp /etc/passwd /opt
[root@localhost ~]# ls /opt
abc.tar.bz2  passwd

[root@thinkmo ~]# yum -y install vsftpd && systemctl start vsftpd && systemctl enable vsftpd && systemctl status vsftpd

#第一条命令执行失败,后边命令不执行
[root@localhost ~]# [ -f /etc/xxoo ] && cp /etc/passwd /opt
[root@localhost ~]# [ -f /etc/xxoo ] && cp /etc/xxoo /opt
[root@localhost ~]# ls /opt

A || B  #当A命令执行失败后才会执行B,如果A执行成功则B不执行
#前便命令执行失败,后边命令则执行
[root@localhost ~]# [ -f /opt/xxoo ] || touch /opt/xxoo.txt
[root@localhost ~]# ls /opt
abc.tar.bz2  passwd  xxoo.txt

#前边命令执行成功,后便命令则不执行
[root@localhost ~]# [ -f /etc/fstab ] || touch /opt/fs.txt
[root@localhost ~]# ls /opt

A ;  B  #执行A命令后执行B,两者没有逻辑关系
[root@localhost ~]# touch /mnt/xx.txt ; touch /opt/xx.txt
[root@localhost ~]# ls /mnt
centos  xx.txt
[root@localhost ~]# ls  /opt
abc.tar.bz2  abc.txt  fs.txt  pass.txt  passwd  xxoo.txt  xx.txt
[root@localhost ~]# touch /opt/oo.txt ; rm -rf /opt/*
[root@localhost ~]# ls /opt
[root@localhost ~]# touch /opt/oo.txt ; rm -rf /opt/* ; touch /opt/xx.txt ; rm -rf /mnt/xx.txt
[root@localhost ~]# ls /opt
xx.txt
[root@localhost ~]# ls /mnt/

if 条件判断结构

  • if 单分支语句,只能判断对,不能判断错
shell
#第一种语法结构
if [条件判断];then
   条件成立时,执行的命令
fi

#第二种语法结构
if [条件判断]
  then
   条件成立时,执行的命令
fi

#if单分支示例
[root@localhost yunwei]# vim if1.sh 
#!/bin/bash
if [ -n /etc/passwd ];then
        echo "非空"
fi

[root@localhost yunwei]# chmod u+x if1.sh 
[root@localhost yunwei]# ./if1.sh 
非空

#只能判断对,不能判断错
[root@localhost yunwei]# vim if1.sh 
#!/bin/bash
if [ -z /etc/passwd ];then
        echo "非空"
fi
  • if 双分支语句
shell
if [条件判断];then
     条件成立时,执行命令a
  else
     条件不成立时,执行命令b
fi

#if双分支示例
[root@localhost yunwei]# vim if2.sh 
#!/bin/bash
if [ -z /etc/passwd ];then
        echo '空值'
  else
        echo '非空'
fi

[root@localhost yunwei]# chmod u+x if2.sh 
[root@localhost yunwei]# ./if2.sh 
非空

#编写参数字的脚本,让计算机产生一个0-9随机数
#$RANDOM环境变量,里边存放的是0-65535之间的随机数
[root@localhost yunwei]# echo $RANDOM
28484
[root@localhost yunwei]# echo $RANDOM
5440
[root@localhost yunwei]# echo $RANDOM
29651
[root@localhost yunwei]# echo $RANDOM
21747
[root@localhost yunwei]# echo $RANDOM
24494

#实用$RANDOM的值对10取余
[root@localhost yunwei]# echo $[RANDOM%10]
4
[root@localhost yunwei]# echo $[RANDOM%10]
2
[root@localhost yunwei]# echo $[RANDOM%10]
5
[root@localhost yunwei]# echo $[RANDOM%10]
3
[root@localhost yunwei]# echo $[RANDOM%10]
9
[root@localhost yunwei]# echo $[RANDOM%10]
1
[root@localhost yunwei]# echo $[RANDOM%10]
0
[root@localhost yunwei]# echo $[RANDOM%10]
3
[root@localhost yunwei]# echo $[RANDOM%10]
2

#编写猜数字脚本
[root@localhost yunwei]# vim if3.sh
#!/bin/bash
read -p '请输入0-9之间的随机数:' num
num1=$[RANDOM%10]
if [ $num -eq $num1 ];then
        echo "恭喜你才对了,奖励一个哇塞女孩!"
 else
        echo "猜错了,请继续努力,奖品是一个哇塞女孩!"
        echo "正确的结果为$num1"
fi

[root@localhost yunwei]# chmod u+x if3.sh 
root@localhost yunwei]# ./if3.sh 
请输入0-9之间的随机数:1
猜错了,请继续努力,奖品是一个哇塞女孩!
正确的结果为6

[root@localhost yunwei]# ./if3.sh 
请输入0-9之间的随机数:7
猜错了,请继续努力,奖品是一个哇塞女孩!
正确的结果为3

[root@localhost yunwei]# ./if3.sh 
请输入0-9之间的随机数:3
猜错了,请继续努力,奖品是一个哇塞女孩!
正确的结果为2

[root@localhost yunwei]# ./if3.sh 
请输入0-9之间的随机数:0
猜错了,请继续努力,奖品是一个哇塞女孩!
正确的结果为5

[root@localhost yunwei]# ./if3.sh 
请输入0-9之间的随机数:9
猜错了,请继续努力,奖品是一个哇塞女孩!
正确的结果为2

[root@localhost yunwei]# ./if3.sh 
请输入0-9之间的随机数:5
猜错了,请继续努力,奖品是一个哇塞女孩!
正确的结果为7

#安装软件包脚本
root@localhost yunwei]# vim if4.sh 
#!/bin/bash
if [ `rpm -q vsftpd &> /dev/null ; echo $?` -eq 0 ];then
        systemctl start vsftpd
        systemctl enable vsftpd &> /dev/null
        echo "软件包以安装,并设置随机自启"
 else
        yum -y install vsftpd &> /dev/null
        systemctl start vsftpd
        systemctl enable vsftpd &> /dev/null
        echo "软件包以重新安装上,并设置随机自启"
fi

[root@localhost yunwei]# chmod u+x if4.sh 
[root@localhost yunwei]# ./if4.sh 
软件包以重新安装上,并设置随机自启
  • if 多分支语句

shell
if [条件判断1];then
      条件1成立时,执行命令a
elif [条件判断2];then
      条件2成立时,执行命令b   
elif [条件判断3];then
      条件3成立时,执行命令c
...省略更多条件
else
      所有条件都不成立时,执行命令d
fi

#编写判断成绩脚本
[root@localhost yunwei]# vim if5.sh
#!/bin/bash
read -p "请输入你的成绩(满分为100分):" 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

[root@localhost yunwei]# chmod u+x if5.sh 
[root@localhost yunwei]# ./if5.sh 
请输入你的成绩:66
勉强及格
[root@localhost yunwei]# ./if5.sh 
请输入你的成绩:55
收拾收拾,回家种地去吧!
[root@localhost yunwei]# ./if5.sh 
请输入你的成绩:0
收拾收拾,回家种地去吧!
[root@localhost yunwei]# ./if5.sh 
请输入你的成绩:99
成绩非常优秀!,奖励一个肤白貌美大长腿,绝世容颜哇塞女孩
[root@localhost yunwei]# vim if5.sh
[root@localhost yunwei]# ./if5.sh 
请输入你的成绩(满分为100分):100
成绩非常优秀!,奖励一个肤白貌美大长腿,绝世容颜哇塞女孩
[root@localhost yunwei]# ./if5.sh 
请输入你的成绩(满分为100分):90
成绩非常优秀!,奖励一个肤白貌美大长腿,绝世容颜哇塞女孩
[root@localhost yunwei]# ./if5.sh 
请输入你的成绩(满分为100分):200
成绩非常优秀!,奖励一个肤白貌美大长腿,绝世容颜哇塞女孩

case条件判断结构

  • case从变量中取值,如果变量中的值与预设的值匹配,则执行对应的命令
shell
#case语法结构
case $变量名 in
值1)
	执行的命令xx;;		#如果变量中的值等于值1,则执行的命令
值2)
	执行的命令yy;;		#如果变量中的值等于2,则执行的命令
...省略其他分支
*)
	执行的命令zz;;		#如果变量中的值都不是以上的值,则执行的命令
esac


[root@localhost yunwei]# vim case.sh
#!/bin/bash
read -p "请输入您喜爱的老师(仓老师|波多老师|小泽老师):" xxoo
case $xxoo in
仓老师)
        echo "又白又嫩又水润,上楼右转1号房间!";;
波多老师)
        echo "前凸后翘,波涛汹涌,上楼右转2号房间!";;
小泽老师)
        echo "肤白貌美大长腿,上楼右转3号房间!";;
*)
        echo "老师休假了";;
esac

[root@localhost yunwei]# chmod u+x case.sh 
[root@localhost yunwei]# ./case.sh 
请输入您喜爱的老师(仓老师|波多老师|小泽老师):仓老师
又白又嫩又水润,上楼右转1号房间!
[root@localhost yunwei]# ./case.sh 波多老师
请输入您喜爱的老师(仓老师|波多老师|小泽老师):波多老师
前凸后翘,波涛汹涌,上楼右转2号房间!
[root@localhost yunwei]# ./case.sh
请输入您喜爱的老师(仓老师|波多老师|小泽老师):小泽老师
肤白貌美大长腿,上楼右转3号房间!
[root@localhost yunwei]# ./case.sh
请输入您喜爱的老师(仓老师|波多老师|小泽老师):穆穆老师
老师休假了

for循环

  • for循环处理,根据变量的取值,重复执行xx命令
shell
#for循环语法结构
for 变量名 in 值1 值2  值3 值N...
do
	执行的命令
done

#编写循环创建用户脚本
[root@localhost yunwei]# vim user.sh
#!/bin/bash
for user in xiaofang xiaowei jiumei alian
do
        useradd $user
        echo "$user 创建成功"
        echo 1 | passwd --stdin $user &> /dev/null
        echo "$user 密码设置成功,初始密码为“1”"
done

[root@localhost yunwei]# chmod u+x user.sh
[root@localhost yunwei]# ./user.sh 
useradd:用户“xiaofang”已存在
xiaofang 创建成功
xiaofang 密码设置成功,初始密码为“1”
xiaowei 创建成功
xiaowei 密码设置成功,初始密码为“1”
jiumei 创建成功
jiumei 密码设置成功,初始密码为“1”
alian 创建成功
alian 密码设置成功,初始密码为“1”

#测试企业的服务器联通性
#!/bin/bash
for i in `seq 254`
do
        ping -c2 -i0.1 -W1 192.168.0.$i &> /dev/null
  if [ $? -eq 0 ];then
        echo "192.168.0.$i UP" 
  else
        echo "192.168.0.$i DOWN" 
  fi
done

[root@localhost yunwei]# chmod u+x ping.sh
[root@localhost yunwei]# ./ping.sh 

#问题:如何将ping的命令结果存储到文件当中,正确的输出结果与错误的输出结果分别单独存储
#!/bin/bash
for i in `seq 254`
do
        ping -c2 -i0.1 -W1 192.168.0.$i >> /dev/null
        if [ $? -eq 0 ];then
           echo "192.168.0.$i UP" >> up.txt
        else
           echo "192.168.0.$i DOWN" >> down.txt
        fi
done

while循环

  • 死循环,只要条件成立就重复执行命令
shell
#while循环语法结构
while 条件判断
do
	执行的命令
done

#编写while循环脚本
[root@localhost yunwei]# vim while.sh
#!/bin/bash
a=1
while [ $a -le 5 ]
do
echo $a
done

[root@localhost yunwei]# chmod u+x while.sh 

#指定循环的固定次数
[root@localhost yunwei]# vim while.sh
#!/bin/bash
a=1
while [ $a -le 5 ]
do
        echo $a
        let a++
        sleep 0.1
done

#使用while循环编写猜数字脚本
[root@localhost yunwei]# vim while1.sh 
#!/bin/bash
num=$[RANDOM%10]
while :
do
           read -p "请输入0-10之间的数字:" num1
        if [ $num -eq $num1 ];then
           echo "恭喜你才对了,奖励一个绝世容颜哇塞女孩!"
           exit  #退出脚本
         else
           echo "请继续努力,离哇塞女孩还差一步了,加油!"
        fi
done


#利用while循环监控网卡进出口流量
[root@localhost ~]# vim net_ens32.sh 
#!/bin/bash
while :
do
clear       #清屏
ifconfig ens32 | head -2      #显示网卡头两行信息
ifconfig ens32 | grep "RX p"  #网卡入口流量
ifconfig ens32 | grep "TX p"  #网卡出口流量
sleep 0.2   #休眠0.2秒
done


#使用while循环编写一个测试整个网段IP地址的联通性脚本
[root@localhost yunwei]# vim while_ping.sh 
#!/bin/bash
i=1  #定义一个变量
while [ $i -le 254 ] #循环判断变量的值小于等于254
do
        IP="192.168.0.$i"   #定义一个变量
        ping -c2 -i0.1 -W1 $IP &> /dev/null #输出结果不要
        if [ $? -eq 0 ];then    #判断上一条命令的结果,如果上一条命令的结果等于0
            echo "$IP UP"       #输出主机UP
        else
           echo "$IP DOWN"      #输出主机DOWN
        fi
   let i++       #对变量的值自加1 
done

shell函数

  • 在shell环境中,将一些需要重复使用的操作,定义为公共的语句块,即可称为函数(给一堆命令取一个别名)
  • 函数可以使脚本中的代码更加简洁,增强易读性,提高脚本的执行效率
shell
#函数定义格式1
function 函数名 {
	执行的命令1
	执行的命令2
	...省略更多命令
}

#函数定义格式2
函数名() {
	执行的命令1
	执行的命令2
	...省略更多命令
}

#定义函数
[root@localhost ~]# net_ens32() {
> ifconfig ens32 | head -2
> ifconfig ens32 | grep "RX p"
> ifconfig ens32 | grep "TX p"
> }

#调用函数
[root@localhost ~]# net_ens32
ens32: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.0.100  netmask 255.255.255.0  broadcast 192.168.0.255
        RX packets 2337  bytes 178268 (174.0 KiB)
        TX packets 2686  bytes 467232 (456.2 KiB)

#定义函数
[root@localhost ~]# myinfo() {
> hostname
> cat /etc/redhat-release 
> uname -r
> free -h
> df -h /
> }

#调用函数
[root@localhost ~]# myinfo
localhost
CentOS Linux release 7.6.1810 (Core) 
3.10.0-957.el7.x86_64
              total        used        free      shared  buff/cache   available
Mem:           972M        393M        193M         13M        385M        374M
Swap:          2.0G          0B        2.0G
文件系统                 容量  已用  可用 已用% 挂载点
/dev/mapper/centos-root   56G  5.2G   51G   10% /

#fork炸弹
[root@localhost yunwei]# vim fork.sh 
#!/bin/bash
.() {
.|. &
}
.

[root@localhost yunwei]# chmod u+x fork.sh 
[root@localhost yunwei]# ./fork.sh

脚本中断及退出

  • break #结束整个循环
  • continue #结束本次循环,进入下一次循环
  • exit #退出脚本
shell
#结束第三次循环,进入下一次循环
#!/bin/bash
for i in {1..5}
do
        [ $i -eq 3 ] && continue
        echo $i
done
echo Over

[root@localhost yunwei]# ./for1.sh 
1
2
4
5
Over


#结束整个循环
#!/bin/bash
for i in {1..5}
do
        [ $i -eq 3 ] && break
        echo $i
done
echo Over

[root@localhost yunwei]# ./for1.sh 
1
2
Over

#直接退出脚本
[root@localhost yunwei]# vim for1.sh
#!/bin/bash
for i in {1..5}
do
        [ $i -eq 3 ] && exit
        echo $i
done
echo Over

[root@localhost yunwei]# ./for1.sh 
1
2

字符串截取

  • 在使用shell脚本完成各种运维任务时,一旦涉及到判断、条件测试等相关操作时往往需要对相关的命令输出进行过滤,提取出符合要求的字符串
  • 字符串截取的常用方法:$
  • ${}截取字符串时,起始位置是从0开始的
shell
[root@localhost yunwei]# phone=13812345678
[root@localhost yunwei]# echo $phone
13812345678

#统计变量的个数
[root@localhost yunwei]# echo ${#phone}
11

#截取变量的前三位,在截取时包含数字本身
[root@localhost yunwei]# echo ${phone:0:3}
138

#截取后四位
[root@localhost yunwei]# echo ${phone:7:4}
5678

#截取中间四位
[root@localhost yunwei]# echo ${phone:3:4}
1234

#截取一个随机的8位密码
[root@localhost yunwei]# vim mima.sh
#!/bin/bash
x=qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789
for i in {1..8}
do
n=$[RANDOM%62]
p=${x:n:1}
pass=$pass$p
done
echo $pass

[root@localhost yunwei]# ./mima.sh 
42iVW9Ba
[root@localhost yunwei]# ./mima.sh 
XuEcVGoR

字符串替换

  • 只替换第一个匹配的结果:$
  • 替换全部匹配的结果:$
shell
#只替换匹配到的第一个字符
[root@localhost yunwei]# echo $phone
13812345678

#将匹配到的第一个3替换成4
[root@localhost yunwei]# echo ${phone/3/4}
14812345678  

#将匹配到的所有3替换成4
[root@localhost yunwei]# echo ${phone//3/4}
14812445678

字符串掐头去尾

  • 从左向右,最短匹配删除:${变量名#*关键词}
  • 从左向右,最长匹配删除:${变量名##*关键词}
  • 从右向左,最短匹配删除:${变量名%关键词*}
  • 从右向左,最长匹配删除:${变量名%%关键词*}
shell
#定义素材
[root@localhost yunwei]# x=`head -1 /etc/passwd`
[root@localhost yunwei]# echo $x
root:x:0:0:root:/root:/bin/bash

#从左到右最短匹配
[root@localhost yunwei]# echo ${x#root}
:x:0:0:root:/root:/bin/bash

#从左到右最短匹配,不加*只匹配最左侧的字串
[root@localhost yunwei]# echo ${x#0}
root:x:0:0:root:/root:/bin/bash

#从左到右最短匹配,加*匹配遇到的第一个字串
[root@localhost yunwei]# echo ${x#*0}
:0:root:/root:/bin/bash

#从左到右最长匹配
[root@localhost yunwei]# echo ${x##0}
root:x:0:0:root:/root:/bin/bash

#从左到右最长匹配
[root@localhost yunwei]# echo ${x##*0}
:root:/root:/bin/bash

#从右到做最短匹配
[root@localhost yunwei]# echo ${x%/bash}
root:x:0:0:root:/root:/bin

#从右到做最短匹配
[root@localhost yunwei]# echo ${x%root*}
root:x:0:0:root:/

#从右向左最长匹配
[root@localhost yunwei]# echo ${x%%root*}

#从右向左最短匹配
[root@localhost yunwei]# echo ${x%0*}
root:x:0:

#从右向左最长匹配
[root@localhost yunwei]# echo ${x%%0*}
root:x:

#把文件以.doc结尾的扩展名,全部改为.txt
[root@localhost yunwei]# touch {1..100}.doc
[root@localhost yunwei]# ls
100.doc  17.doc  24.doc  31.doc  39.doc  46.doc  53.doc  60.doc  68.doc  75.doc  82.doc  8.doc   97.doc   if2.sh     while.sh
10.doc   18.doc  25.doc  32.doc  3.doc   47.doc  54.doc  61.doc  69.doc  76.doc  83.doc  90.doc  98.doc   if3.sh
11.doc   19.doc  26.doc  33.doc  40.doc  48.doc  55.doc  62.doc  6.doc   77.doc  84.doc  91.doc  99.doc   if4.sh
12.doc   1.doc   27.doc  34.doc  41.doc  49.doc  56.doc  63.doc  70.doc  78.doc  85.doc  92.doc  9.doc    if5.sh
13.doc   20.doc  28.doc  35.doc  42.doc  4.doc   57.doc  64.doc  71.doc  79.doc  86.doc  93.doc  case.sh  mima.sh
14.doc   21.doc  29.doc  36.doc  43.doc  50.doc  58.doc  65.doc  72.doc  7.doc   87.doc  94.doc  for1.sh  ping.sh
15.doc   22.doc  2.doc   37.doc  44.doc  51.doc  59.doc  66.doc  73.doc  80.doc  88.doc  95.doc  fork.sh  user.sh
16.doc   23.doc  30.doc  38.doc  45.doc  52.doc  5.doc   67.doc  74.doc  81.doc  89.doc  96.doc  if1.sh   while1.sh

#字符串去尾方式批量修改文件扩展名
[root@localhost yunwei]# vim file.sh 
#!/bin/bash
for i in `ls *.doc`
do
        mv $i ${i%doc}txt
done

#字符串替换方式批修改文件扩展名
[root@localhost yunwei]# vim file.sh 
#!/bin/bash
for i in `ls *.txt`
do
        mv $i ${i/txt/doc}
done

shell数组

shell
#定义数组方式一:数组名=(值1 值2 值3 .. .. 值n)
[root@localhost yunwei]# x=(11 22 33 44 55 abc)
[root@localhost yunwei]# echo $x
11

#按照下标取值,下标从0起使,0就是数据内的第一个值
[root@localhost yunwei]# echo ${x[0]}
11
[root@localhost yunwei]# echo ${x[0]}
11
[root@localhost yunwei]# echo ${x[1]}
22
[root@localhost yunwei]# echo ${x[2]}
33
[root@localhost yunwei]# echo ${x[3]}
44
[root@localhost yunwei]# echo ${x[4]}
55

#获取数组内所有值
[root@localhost yunwei]# echo ${x[@]}
11 22 33 44 55 abc

#定义数组方式二:数组名[下标]=值
[root@localhost yunwei]# b[0]=aa
[root@localhost yunwei]# b[1]=xx
[root@localhost yunwei]# b[2]=xxoo
[root@localhost yunwei]# echo ${b[0]}
aa
[root@localhost yunwei]# echo ${b[1]}
xx
[root@localhost yunwei]# echo ${b[2]}
xxoo
[root@localhost yunwei]# echo ${b[@]}
aa xx xxoo
[root@localhost yunwei]# echo ${b[*]}
aa xx xxoo

正则表达式

  • 正则表达式使用一串符号描述有共同属性的数据
基本正则符号描述
^匹配行首
$匹配行尾
[]集合,匹配集合中的任意单个字符
[^]对集合取反
.匹配任意单个字符
*匹配前一个字符出现的任意次数[*不允许单独[使用]
\{n,m\}匹配前一个字符 n 到 m 次
\{n\}匹配前一个字符 n 次
shell
#匹配以什么什么开头的行
[root@localhost yunwei]# grep "^root" /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@localhost yunwei]# grep "^#" /etc/fstab

#匹配以什么什么结尾的行
[root@localhost yunwei]# grep "bash$" /etc/passwd

[root@localhost yunwei]# grep -v "^#" /etc/fstab
[root@localhost yunwei]# grep -v "^#" /etc/fstab | grep -v "^$" 

#集合,匹配集合中的任意单个字符
[root@localhost yunwei]# grep "ro[abcotabcd]" /etc/passwd

#集合,对集合中的任意单个字符取反
[root@localhost yunwei]# grep "ro[^abcotabcd]" /etc/passwd

#匹配任意单个字符
[root@localhost yunwei]# grep "roo." /etc/passwd
[root@localhost yunwei]# grep ".oot" /etc/passwd

[root@localhost yunwei]# grep "w.*" /etc/passwd

#准备素材
[root@localhost yunwei]# vim 1.txt 
a
b
ab
aab
acb
adb
amnb
amnbmnbmnb
aaaabaaaaaaabaaaaaaaaaaaaab
aabb  the  ccdd
abcthe
thexxoo
xxootheooxx

[root@localhost yunwei]# grep "a*b" 1.txt
[root@localhost yunwei]# grep "a.*b" 1.txt
[root@localhost yunwei]# grep ".*b" 1.txt
[root@localhost yunwei]# grep "a.*" 1.txt

#匹配前一个字符至少出现一次以上,\{n,m\}
[root@localhost yunwei]# grep "a\{1,\}" 1.txt

#匹配前一个字符  n 次,\{n\}
[root@localhost yunwei]# grep "o\{2\}" /etc/passwd
[root@localhost yunwei]# grep "a\{2\}" 1.txt
**扩展正则符号****描述**
+最少匹配一次
最多匹配一次
{n,m}匹配 n 到 m 次
()组合为整体,保留(复制)
|或者
\b单词边界
shell
#基本正则用法
[root@localhost yunwei]# grep "a\{2,4\}b" 1.txt

#使用扩展正则时,需要使用egrep命令过滤
[root@localhost yunwei]# egrep "a{2,4}b" 1.txt

#匹配前一个字符至少出现一次以上
[root@localhost yunwei]# egrep "a+" 1.txt

#基本正则实现方式,匹配前一个字符至少出现一次以上
[root@localhost yunwei]# grep "a\{1,\}" 1.txt

#匹配前一个字符出现了0次或一次
[root@localhost yunwei]# egrep "a?b" 1.txt

#基本正则实现方式,匹配前一个字符出现了0次或一次
[root@localhost yunwei]# grep "a\{0,1\}b" 1.txt
[root@thinkmo ~]# egrep 'a{1,2}' 1.txt 

#或者
[root@localhost yunwei]# egrep "ab|acb|aaab" 1.txt

#组合为整体
[root@localhost yunwei]# egrep "(ab)" 1.txt
ab
aab
aaaabaaaaaaabaaaaaaaaaaaaab
[root@localhost yunwei]# egrep "(acb)" 1.txt
acb
[root@localhost yunwei]# egrep "(aaab)" 1.txt

#单词边界
[root@localhost yunwei]# grep "the" 1.txt
aabb  the  ccdd
abcthe
thexxoo
xxootheooxx
[root@localhost yunwei]# egrep "\bthe\b" 1.txt
aabb  the  ccdd
[root@localhost yunwei]# egrep "\bthe" 1.txt
aabb  the  ccdd
thexxoo
[root@localhost yunwei]# egrep "the\b" 1.txt
aabb  the  ccdd
abcthe

sed流式编辑器

  • sed是一个非交互的文本编辑器,实现的功能跟vim相同,主要是对文件内容进行输出、删除、替换、复制、剪切、导入、导出等功能
  • 命令格式1:前置命令 | sed [选项] '[指令]' 文件名
  • 命令格式2:sed [选项] '[指令]' 文件名
  • 常用选项:
    • -n #屏蔽默认输出,默认sed会将所有的输出结果输出到屏幕中,-n只把sed处理的行输出到屏幕
    • -i #直接修改文件内容,如果不加-i选项,并不会真正改变文件的内容
    • -r #使用扩展正则,若与其他选项连用应作为首个选项
  • 动作指令:
    • p #打印指定的行,如:2,4p 打印第234行,如:2p;4p 打印第2行与第4行
    • d #删除指定的行,如:2,4d 删除第234行
    • s #字符串替换,如:s/旧字串/新字串/
    • r #导入文件内容,如:4r 1.txt 在第4行下导入1.txt文件内容
    • w #导出文件内容,如:3w 1.txt 将文件第三行内容另存到2.txt文件中
shell
#打印文件第一行内容
[root@localhost yunwei]# sed -n '1p' /etc/passwd
root:x:0:0:root:/root:/bin/bash

#打印文件第三行内容
[root@localhost yunwei]# sed -n '3p' /etc/passwd
daemon:x:2:2:daemon:/sbin:/sbin/nologin

#打印文件第3行到6行
[root@localhost yunwei]# sed -n '3,6p' /etc/passwd
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync

#打印文件第3行到6行
[root@localhost yunwei]# head -6 /etc/passwd | tail -4
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync

#利用正则表达式匹配以root开头的行(正则表达式要放在//内)
[root@localhost yunwei]# sed -n '/^root/p' /etc/passwd

#匹配以bash结尾的行
[root@localhost yunwei]# sed -n '/bash$/p' /etc/passwd
[root@localhost yunwei]# sed -n '/nologin$/p' /etc/passwd

#打印文件最后一行,打印行号
[root@localhost yunwei]# sed -n '$=' /etc/passwd
70

#打印以root开头的行号
[root@localhost yunwei]# sed -n '/^root/=' /etc/passwd
1

#拷贝文件练习
[root@localhost opt]# cp /etc/passwd /opt/test

#删除文件2到4行
[root@localhost opt]# sed '2,4d' test | wc -l
67

#使用i选项直接修改源文件
[root@localhost opt]# sed -i '2,4d' test 

#使用分号分隔
[root@localhost opt]# sed -i '10d;12d' test
[root@localhost opt]# sed -n '$=' test
62

#制作素材
[root@localhost opt]# vim 1.txt 
aaaabbbbb
bbbbbaaaa
cccccaaaaa
dddddaaaa


[root@localhost opt]# sed -n '/aaaa/p' 1.txt
aaaabbbbb
bbbbbaaaa
cccccaaaaa
dddddaaaa

#取反删除
[root@localhost opt]# sed -i '/cccc/!d' 1.txt 
[root@localhost opt]# cat 1.txt 
ccccaaaa

[root@localhost opt]# vim 1.txt 
aaaabbbb
ddddaaaa
zzzzaaaa
ccccaaaa

#删除以ccc开头的行
[root@localhost opt]# sed -i '/^cccc/d' 1.txt
[root@localhost opt]# cat 1.txt 
aaaabbbb
ddddaaaa
zzzzaaaa

#删除空行
[root@localhost opt]# sed -i '/^$/d' 1.txt 
[root@localhost opt]# cat 1.txt 

#准备素材
[root@localhost opt]# vim xx.txt
2021 2020 2019 2018
2021 2021 2020 2019
2021 2022 2021 2020

#替换文件每一行匹配到的第一个字串
[root@localhost opt]# sed 's/2021/xxxx/' xx.txt 
xxxx 2020 2019 2018
xxxx 2021 2020 2019
xxxx 2022 2021 2020

#替换文件每一行匹配到的第二个字串
[root@localhost opt]# sed 's/2021/xxxx/2' xx.txt 
2021 2020 2019 2018
2021 xxxx 2020 2019
2021 2022 xxxx 2020

#替换文件每一行匹配到的所有指定字串
[root@localhost opt]# sed 's/2021/xxxx/g' xx.txt 
xxxx 2020 2019 2018
xxxx xxxx 2020 2019
xxxx 2022 xxxx 2020

#将匹配到的第一个字串替换成空
[root@localhost opt]# sed 's/2021//' xx.txt 
 2020 2019 2018
 2021 2020 2019
 2022 2021 2020
 
 #将匹配到的第=二字串替换成空
[root@localhost opt]# sed 's/2021//2' xx.txt 
2021 2020 2019 2018
2021  2020 2019
2021 2022  2020

#替换时屏蔽默认输出
[root@localhost opt]# sed -n 's/root/xxoo/g' test
[root@localhost opt]# sed -n 's/root/xxoo/gp' test
xxoo:x:0:0:xxoo:/xxoo:/bin/bash
operator:x:11:0:operator:/xxoo:/sbin/nologin

#替换符可以使用任意的特殊符号
[root@localhost opt]# sed  's#2021#xxoo#' xx.txt 
xxoo 2020 2019 2018
xxoo 2021 2020 2019
xxoo 2022 2021 2020
[root@localhost opt]# sed  's,2021,xxoo,' xx.txt 
xxoo 2020 2019 2018
xxoo 2021 2020 2019
xxoo 2022 2021 2020
[root@localhost opt]# sed  's!2021!xxoo!' xx.txt 
xxoo 2020 2019 2018
xxoo 2021 2020 2019
xxoo 2022 2021 2020
[root@localhost opt]# sed  's;2021;xxoo;' xx.txt 
xxoo 2020 2019 2018
xxoo 2021 2020 2019
xxoo 2022 2021 2020
[root@localhost opt]# sed  's*2021*xxoo*' xx.txt 
xxoo 2020 2019 2018
xxoo 2021 2020 2019
xxoo 2022 2021 2020
[root@localhost opt]# sed  's:2021:xxoo:' xx.txt 
xxoo 2020 2019 2018
xxoo 2021 2020 2019
xxoo 2022 2021 2020

#将文件中/bin/bash替换成/bin/sh
[root@localhost opt]# sed -n 's/\/bin\/bash/\/bin\/sh/gp' test
[root@localhost opt]# sed -n 's,/bin/bash,/bin/sh,gp' test

#使用sed给文件1-7行批量添加注释
[root@localhost opt]# sed -n '1,7s/^/#/p' test 

#使用sed给文件1-7行批量添加注释,直接修改源文件
[root@localhost opt]# sed -i '1,7s/^/#/' test 

#批量去除1-7行的注释
[root@localhost opt]# sed -n '1,7s/^#//' test

#批量去除1-7行的注释,直接修改源文件
[root@localhost opt]# sed -i '1,7s/^#//' test

#将文件中所有的数字替换成空
[root@localhost opt]# sed -n 's/[0-9]//gp' test

#将文件中所有的英文字母替换成空
[root@localhost opt]# sed -n 's/[a-Z]//gp' test

#准备素材
[root@localhost opt]# vim a.txt
xxxxxxx
yyyyyyy
zzzzzz

#将a.txt文件内容导入到test文件中,默认读一行导一遍
[root@localhost opt]# sed 'r/opt/a.txt' test

#指定导入的行,导入到test文件第一行下边
[root@localhost opt]# sed '1r/opt/a.txt' test
root:x:0:0:root:/root:/bin/bash
xxxxxxx
yyyyyyy
zzzzzz

#指定连续导入的行
[root@localhost opt]# sed '1,3r/opt/a.txt' test
root:x:0:0:root:/root:/bin/bash
xxxxxxx
yyyyyyy
zzzzzz

root:x:0:0:root:/root:/bin/bash
xxxxxxx
yyyyyyy
zzzzzz

root:x:0:0:root:/root:/bin/bash
xxxxxxx
yyyyyyy
zzzzzz

#将a.txt文件第一行内容导出到b.txt文件中
[root@localhost opt]# sed '1w b.txt' a.txt
[root@localhost opt]# cat b.txt 
xxxxxxx

awk编程语言

  • awk编程语言/数据处理引擎

  • 创造者:Aho Winberger Kernighan

  • 基于模式匹配检查输入文本,逐行处理并输出,获取指定的数据

  • awk过滤数据时支持仅打印某一列,如:第2列、第4列...

  • awk命令格式1:awk [选项] '条件1{指令} 条件2{指令}' 文件名

  • awk命令格式2:前置命令 | awk [选项] '条件{指令}'

  • 常用指令:print 是最常用的打印指令

  • 常用选项:-F #指定分隔符,如不指定分隔符,默认以空格或tab键为默认分隔符,可通过[]集合匹配多种单个字符

  • awk内置变量:$1第一列,$2第二列,$3第三列,依次类推,NR文件当前行号,NF文件当前列数

  • 命令格式1示例:

shell
#准备素材
[root@localhost opt]# vim test.txt
hello the woman
welcome to china

#打印文件的第一列
[root@localhost opt]# awk '{print $1}' test.txt 
hello
welcome

#打印文件第一列和第三列
[root@localhost opt]# awk '{print $1,$3}' test.txt 
hello woman
welcome china

#打印passwd文件第一列(默认没有空格与tab键作为分隔符,打印文件所有内容)
[root@localhost opt]# awk '{print $1}' /etc/passwd

#手动指定以:作为分隔,打印文件第一列
[root@localhost opt]# awk -F: '{print $1}' /etc/passwd

#打印文件第一列与第七列
[root@localhost opt]# awk -F: '{print $1,$7}' /etc/passwd

#通过[]集合匹配多种单个字符作为分隔符,打印用户名与解释器字段
[root@localhost opt]# awk -F[:/] '{print $1,$10}' /etc/passwd

#通过正则表达式过滤以root开头的行
[root@localhost opt]# awk -F: '/^root/{print}' /etc/passwd
root:x:0:0:root:/root:/bin/bash

#通过正则表达式过滤以root开头的行,打印第1列与第7列
[root@localhost opt]# awk -F: '/^root/ {print $1,$7}' /etc/passwd
root /bin/bash

#打印文件每一行与每一行的列数
[root@localhost opt]# awk -F: '{print NR,NF}' /etc/passwd

#打印文件每一行与每一行的列数,并打印最后一列
[root@localhost opt]# awk -F: '{print NR,NF,$NF}' /etc/passwd

#通过常量打印执行的列
[root@localhost opt]# awk -F: '{print $1,"用户的解释器为:",$7}' /etc/passwd
root 用户的解释器为: /bin/bash

#匹配第一列包含root的行
[root@localhost opt]# awk -F: '$1~/root/' /etc/passwd
root:x:0:0:root:/root:/bin/bash

#排除第7列noloogin的行,打印第1列与第7列
[root@localhost opt]# awk -F: '$7!~/nologin/{print $1,$7}' /etc/passwd

#利用扩展正则过滤,以root或者adm开头的行,打印第1列与第7列
[root@localhost opt]# awk -F: '/^(root|adm)/{print $1,$7}' /etc/passwd
root /bin/bash
adm /sbin/nologin
  • awk使用数值/字符串比较设置条件
  • 等于: ==
  • 不等于: !=
  • 大于: >
  • 大于等于: >=
  • 小于: <
  • 小于等于:<=
shell
#打印行号等于3
[root@localhost opt]# awk 'NR==3{print}' /etc/passwd
daemon:x:2:2:daemon:/sbin:/sbin/nologin

#使用sed更加方便打印某一行
[root@localhost opt]# sed -n '3p' /etc/passwd
daemon:x:2:2:daemon:/sbin:/sbin/nologin

#打印文件中第3列大于等于1000,打印第1列,第3列,第7列
[root@localhost opt]# awk -F: '$3>=1000{print $1,$3,$7}' /etc/passwd
lisi 1000 /bin/bash

#打印文件中第三列小于1000,打印第1列,第3列,第7列
[root@localhost opt]# awk -F: '$3<1000{print $1,$3,$7}' /etc/passwd
root 0 /bin/bash

#打印文件中第3列大于500并且小于1000,打印第1列,第3列,第7列
[root@localhost opt]# awk -F: '$3>500 && $3<1000 {print $1,$3,$7}' /etc/passwd
polkitd 999 /sbin/nologin

#打印第一列不等于root的行
[root@localhost opt]# awk -F: '$1!="root"{print}' /etc/passwd
  • awk过滤时机:awk ‘BEGIN{指令} {指令} END{指令}’ 文件名
    • BEGIN{指令} #读取文件内容之前执行指令,指令执行一次,行前处理
    • {指令} #读取文件过程中执行,指令逐行执行,读一行,执行一次
    • END{指令} #读取文件内容结束后执行指令,指令执行一次,行后处理
shell
#BEGIN{指令}行前处理
[root@localhost opt]# awk 'BEGIN{print "正在处理中"}'
正在处理中

#定义变量
[root@localhost opt]# awk "BEGIN{x=10;print x}"
10

#四则运算
[root@localhost opt]# awk "BEGIN{x=10;print x+5}"
15
[root@localhost opt]# awk "BEGIN{x=10;print x+5}"
15
[root@localhost opt]# awk "BEGIN{x=10;print x-5}"
5
[root@localhost opt]# awk "BEGIN{x=10;print x*5}"
50
[root@localhost opt]# awk "BEGIN{x=10;print x/5}"
2

[root@localhost opt]# awk "BEGIN{print 10+10}"
20
[root@localhost opt]# awk "BEGIN{print 10-5}"
5
[root@localhost opt]# awk "BEGIN{print 10*2}"
20
[root@localhost opt]# awk "BEGIN{print 10/3}"
3.33333
[root@localhost opt]# awk "BEGIN{print 10%3}"
1

#通过awk统计系统里使用bash解释器的用户有什么个?
[root@localhost opt]# awk 'BEGIN{x=0}/bash$/{x++}END{print x}' /etc/passwd
27
[root@localhost opt]# awk '/bash$/{x++}END{print x}' /etc/passwd
27
  • awk分支结构
  • if单分支格式:if(条件){指令}
  • if双分支格式:if(条件){指令}else{指令}
shell
#if单分支统计passwd文件中UID大于或等于1000的用户个数
awk  -F: '{if($3>=1000){x++}} END{print x}' /etc/passwd
if($3>=1000){x++}
[root@localhost ~]# awk -F: '{if($3>=1000){x++}}END{print x}' /etc/passwd

#if双分支统计passwd文件中UID大于等于1000的用户,和小于1000的用户个数
[root@localhost ~]# awk -F: '{if($3>=1000){i++} else{x++}} END{print i,x}' /etc/passwd
  • awk数组
  • 定义数组格式1:数组名[下标]=值
  • 定义数组格式2:数组名=值
  • 数组的用法:for(变量名 in 数组名) {print 数组名[变量]}
shell
#awk定义数组方式
[root@localhost ~]# awk 'BEGIN{x[0]=10;x[1]=20;print x[0],x[1]}'
10 20

#awk定义数组方式
[root@localhost ~]# awk 'BEGIN{x[0]++;print x[0]}'
1
  • awk循环结构
  • 命令格式:for(变量名 in 数组名){print 数组名[变量]}
shell
[root@localhost ~]# awk 'BEGIN{a[0]=00;a[1]=11;a[2]=22;for(i in a){print i,a[i]}}'
0 0
1 11
2 22
  • awk命令格式2:前置命令 | awk [选项] '条件{指令}'
shell
#通过awk打印剩余内存
[root@localhost opt]# free -h | grep Mem | awk '{print $4}'
134M
[root@localhost opt]# free -h | awk '/Mem/{print $4}'
134M

#用awk写一个监控脚本,监控网卡的进出口流量
[root@localhost ~]# vim while_liuliang.sh 
#!/bin/bash
while :
do
        clear
        ifconfig ens32 | awk '/inet /{print "IP:",$2}'
        ifconfig ens32 | awk '/RX p/{print "入口流量:",$5}'
        ifconfig ens32 | awk '/TX p/{print "出口流量:",$5}'
        sleep 0.1
done

#使用awk过滤系统根分区使用情况
[root@localhost ~]# df -h | grep '/$' | awk '{print $4}' | awk -FG '{print $1}'
51

#过滤根分区剩余空间与物理内存空间
[root@localhost ~]# vim df_free.sh
#!/bin/bash
df -h | grep '/$' | awk '{print "根分区剩余空间:",$4}'
free -h | grep Mem | awk '{print "物理内存剩余空间:", $4}'

[root@thinkmo ~]# df -h | grep /$ | awk '{print $4}' | awk -FG '{print $1}'
13

#通过awk统计用户登录系统的次数
[root@localhost ~]# who | awk '{ip[$1]++}END{for(i in ip)print i,ip[i]}'
root 1