yuanxiaojiang
人的放纵是本能,自律才是修行

条件表达式

格式

  test 或 [ ]
[[ ]] 或 (( )) 
共同点 都可以用于判断 都可以用于判断
区别 仅支持普通判断,不支持正则 支持普通判断和正则

表示的逻辑关系符号不同

-a  -o  !  -  gt

&&  ||  !  >  <  <=  >=
应用场景 大部分情况下使用 [] 进行判断 需要使用正则时使用

判断文件

条件表达式-文件 说明
-f
file 判断是否存在,判断是否为文件
-d
dir 判断是否存在,判断是否为目录
-x
executable 判断是否存在,是否有执行权限
-s
size 判断是否存在,文件是否为空(非空为真)
-r
是否有读权限
-w
是否有写权限
-nt
newer than 两个文件修改时间 是否更加新
-ot
older than 两个文件修改时间 是否更加老
-L
软连接
-e
是否存在(任何类型文件)
# 条件测试语句,不会直接提示对错
[ -f /etc/hosts ]
echo $?

# 判断文件是否存在且为指定的格式
[ -d /etc/ ] && echo "成立" || echo "失败"

# 检查/etc/rc.d/rc.local是否有执行权限
[ -x /etc/rc.d/rc.local ] && echo "成立" || echo "失败"

# 检查ip命令是否有执行权限,如果没有则退出
[ -x /sbin/ip ] || exit 1

# 检查/etc/hosts文件是否为空
[ -s /etc/hosts ] && echo "非空" || echo ""

字符串对比

条件表达式-字符串对比 说明
"str1" = "str2" 两字符串是否相等
"str1" != "str2" 两字符串是否不相等
-z "str" zero  检查str字符串是否为空
-n "str" not zero  检查str字符串是否为非空
# 小技巧:在进行字符串比较的时候尾巴上可以加个x,防止变量为空,造成匹配/执行失败
[root@m01 ~]# str1="yuan"
[root@m01 ~]# str2="yuan"
[root@m01 ~]# [ "${str1}"x = "${str2}"x ] && echo "相同" || echo "不同"
相同

 

大小比较(仅支持整数对比,不支持小数对比

  [ ] [[ ]]
大于 -gt  great than >
大于等于 -ge  great equal >=
小于 -lt  less than <
小于等于 -le  less equal <=
等于 -eq  equal ==
不等于 -ne  not equal !=

逻辑判断

  [ ] [[ ]]
与(同时成立)
-a &&
或(只有有个成立的就行)
-o ||
非(取反)
! !

正则判断(=~)

[root@m01 ~]# num=666
[root@m01 ~]# [[ $num =~ [0-9] ]] && echo "成立" || echo "失败"
成立
[root@m01 ~]# num=yuan666
[root@m01 ~]# [[ $num =~ [0-9] ]] && echo "成立" || echo "失败"
成立    # 仅仅表示变量中只要有数字就行
[root@m01 ~]# [[ $num =~ ^[0-9]+$ ]] && echo "成立" || echo "失败"
失败

 

if

单分支if判断

if 条件;then
    满足条件后执行的内容
fi

if 条件
then
    满足条件后执行的内容
fi

 

双分支判断

if 条件;then
   满足条件后执行的内容。
else
   不满足条件执行的内容。
fi

 

案例:检查根分区磁盘空间使用率

如果大于等于80%,提示磁盘空间不足,否则提示磁盘空间正常

[root@m01 /server/scripts/devops-shell]# cat 04.sh 
root_usage=`df -h |grep -w '/' |awk -F'[ %]+' '{print $(NF-1)}'`
if [ $root_usage -ge 80 ];then
    echo "磁盘空间不足,请速速删根"
else
    echo "磁盘空间正常"
fi

 

多分支判断

if 条件;then
   满足条件后执行的内容。
elif 条件;then   #else if 
   满足elif条件,执行的内容。
elif 条件;then
   满足elif条件,执行的内容。   
else
   不满足条件执行的内容。
fi

 

案例:输出指定用户的信息

  • 是否输入了用户名
  • 用户名是否存在,如果不存在则提示用户不存在且退出
  • 如果用户存在则输出以下内容
    • 是否可以登录
    • 用户uid、gid
    • 用户家目录
    • 最近一次登录的情况
[root@m01 /server/scripts/devops-shell]# cat 05.sh 
read -p "请输入用户名:" user
[ "${user}x" = "x" ] && {
    echo "用户名不能为空"
    exit 1
}
id $user &>>/dev/null
if [ $? -ne 0 ];then
    echo "${user}用户不存在"
    exit 2
fi
is_log=`grep "^${user}" /etc/passwd |awk -F'[:]' '{print $NF}'`
if [ "${is_log}" = "/bin/bash" ];then
    echo "${user}可以登录"
else
    echo "${user}不可以登录"
fi

id "${user}" |awk -F'[ ]' '{print $1,$2}'
grep "^${user}" /etc/passwd |awk -F'[:]' '{print $6}'
lastlog |grep "^${user}"

 

case语句

条件分支语句,一般用于实现多种选择的脚本

格式

case variable in  
    pattern1)  
        # 当 variable 与 pattern1 匹配时执行的命令  
        ;;  
    pattern2)  
        # 当 variable 与 pattern2 匹配时执行的命令  
        ;;  
    *)  
        # 这个部分是默认情况(所有其他情况)  
        ;;  
esac  

 

案例:文件管理(文件的增删改查)脚本

[root@m01 /server/scripts/devops-shell]# cat file_manager.sh 
#!/bin/bash 
echo "请选择一个操作:"  
echo "1) 创建文件"  
echo "2) 删除文件"  
echo "3) 显示文件内容"  
read -p "请输入操作编号 (1-3): " choice  

case $choice in  
    1)  
        # 创建文件  
        read -p "请输入要创建的文件名称: " filename  
        touch "$filename"  
        echo "文件 '$filename' 已创建."  
        ;;  
    2)  
        # 删除文件  
        read -p "请输入要删除的文件名称: " filename  
        if [ -f "$filename" ]; then  
            rm "$filename"  
            echo "文件 '$filename' 已删除."  
        else  
            echo "文件 '$filename' 不存在."  
        fi  
        ;;  
    3)  
        # 显示文件内容  
        read -p "请输入要显示内容的文件名称: " filename  
        if [ -f "$filename" ]; then  
            cat "$filename"  
        else  
            echo "文件 '$filename' 不存在."  
        fi  
        ;;  
    *)  
        echo "无效的操作编号."  
        ;;  
esac  

 

案例:判断用户输入的是yes还是no

[root@m01 /server/scripts/devops-shell]# cat yes_no.sh 
#!/bin/bash
read -p "input yes or no:" choice
case "${choice}" in 
    yes|y|Y|Yes|YES) echo "您输入的是yes";;
    no|n|N\No|NO) echo "您输入的是no";;
esac

函数

格式

# 格式一
function 函数名() {
  命令,内容
  return n 函数大的返回值
}
# 格式二
函数名(){}
# 格式三
function 函数名 {}

 

 

函数传参

位置参数 shell脚本 函数
$n 脚本的第n个参数 函数的第n个参数
$0 脚本的名字 脚本的名字
$# 脚本参数的个数 函数参数的个数
$@ / $* 脚本所有的参数 函数所有的参数
[root@m01 /server/scripts/devops-shell]# cat function.sh 
show(){
cat <<EOF
  show函数的参数个数:$#
  show函数的所有参数:$@
  脚本的名字:$0
  脚本的第一个参数:$1
EOF
}
show yuan xiao jiang
[root@m01 /server/scripts/devops-shell]# sh function.sh 
  show函数的参数个数:3
  show函数的所有参数:yuan xiao jiang
  脚本的名字:function.sh
  脚本的第一个参数:yuan

 

案例:sersync服务管理脚本

目标:书写服务管理脚本

/server/scripts/xxx {star|stop|restart|status}

服务是编译安装,二进制包

[root@nfs01 /server/scripts/devops-shell]# cat sersync_manage.py 
#!/bin/bash
choice=$1
sersync_start() {
  sersync2 -rdo /app/tools/sersync/conf/confxml.xml
}

sersync_stop() {
  sersync_pid=`ps -ef |grep sersync2 |grep -v grep |awk '{print $2}'`
  kill $sersync_pid
}

sersync_restart() {
  sersync_stop
  sersync_start
}

sersync_status() {
  # 判断服务是否运行
  sersync_count=`ps -ef |grep sersync2 |grep -v grep |wc -l`
  if [ $sersync_count -eq 0 ];then
    echo "sersync is no running"
  else
    sersync_pid=`ps -ef |grep sersync2 |grep -v grep |awk '{print $2}'`
    echo "sersync is running ,pid id ${sersync_pid}"
  fi
}

error_msg() {
  echo "please input:#0 {start|stop|restart|status}"
}

case "$choice" in 
    start) sersync_start ;;
    stop) sersync_stop ;;
    restart) sersync_restart ;;
    status) sersync_status ;;
    *) error_msg
esac

 

必知必会核心命令

脚本常用监控命令

端口 一般用于检查端口是否存在,是否能连接

ss -lntup 或 netstat -lntup 或 lsof -i:80

是否能连接:telnet  nc  nmap

进程 检查进程状态 ps  top  iotop
网络 检查连通性 ping  iftop(流量)  dig(DNS)
web http请求 curl  wget
系统全能 atop  

端口检查

  • 检查端口是否存在
# 检查端口是否存在
[root@m01 ~]# ss -lntup |grep 22
tcp    LISTEN     0      128       *:22                    *:*                   users:(("sshd",pid=1409,fd=3))
tcp    LISTEN     0      128    [::]:22                 [::]:*                   users:(("sshd",pid=1409,fd=4))
[root@m01 ~]# netstat -lntup |grep 22
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1409/sshd           
tcp6       0      0 :::22 
[root@m01 ~]# lsof -i:22
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
sshd    1409 root    3u  IPv4  23401      0t0  TCP *:ssh (LISTEN)
sshd    1409 root    4u  IPv6  23403      0t0  TCP *:ssh (LISTEN)
sshd    1657 root    3u  IPv4  24182      0t0  TCP m01:ssh->10.0.0.1:40118 (ESTABLISHED)
sshd    2743 root    3u  IPv4  43241      0t0  TCP m01:ssh->10.0.0.1:17163 (ESTABLISHED)

 

  • 检查端口是否能访问
# telnet 
    -e:用于指定一个发送特殊命令的字符(通常是单个字符)
    -q:用来禁用输出某些特定的信息(减少不必要的输出)
    telnet -e <escape_character> <hostname> <port>  
[root@m01 ~]# telnet -eq i 10.0.0.61 22
Telnet escape character is 'q'.
Usage: telnet [-4] [-6] [-8] [-E] [-L] [-S tos] [-a] [-c] [-d] [-e char] [-l user]
    [-n tracefile] [-b hostalias ] [-r] 
 [host-name [port]]

# nc(-z:无io模式)
  # 作为tcp客户端
    nc <hostname> <port>
  # 作为tcp服务端
    nc -l -p <port>
  # 发送文件
    nc <hostname> <port> < file_to_send

 

 

web与api测试命令

# wget
    -t  失败重新范文次数
    -T  timeout 超时时间
    -q  不显示wget输出
    --spider  不下载文件,仅访问

curl
  -v  显示详细信息
  -L  跟随跳转 
  -H  修改请求头 
  -I  只显示响应头 
  -w  按照指定格式输出 
  -o  输出指定到文件或空 
  -s  静止显示进度条或错误消息
-X 指定请求方法

 

案例:检查指定的地址的端口是否可以访问

sh check_access.sh 10.0.0.61 22

屏幕输出可以访问或不可以访问

#!/bin/bash
#description: 使用nmap命令检测指定的地址的端口是否可以访问
. /etc/init.d/functions
#1.vars
url=$1
port=$2
#2.检查
function check_parameters() {
#2.1检查参数的个数是否为2
if [ $# -ne 2 ];then
  action "请输入两个参数:url 端口" /bin/false
  exit 1
fi
#2.2检查输入的url和端口
[[ $url =~ ^[0-9a-zA-Z.:/]+$ ]] || {
  action "请输入正确的url格式:" /bin/false
  exit 2
}
[[ $port =~ ^[0-9]+$ ]] || {
  action "请输入正确的port格式:" /bin/false
  exit 3
}
}
# 3.检查指定的地址+端口是否可以访问
function check_url_port() {
url_port_count=`nmap -p $port $url |grep -wc open`
if [ $url_port_count -eq 1 ];then
  action "$url:$port 能访问" /bin/true
else
  action "$url:$port 不能访问" /bin/false
fi
}

check_parameters $1 $2 # 由于check_parameters需要检测参数的个数,所以需要传参
check_url_port

 

 

telnet

[root@m01 /server/scripts/devops-shell]# echo q |telnet -e q www.baidu.com 80
Telnet escape character is 'q'.
Trying 183.2.172.177...
Connected to www.baidu.com.
Escape character is 'q'.

telnet> Connection closed.
[root@m01 /server/scripts/devops-shell]# echo $?
0
[root@m01 /server/scripts/devops-shell]# echo q |telnet -e q www.yuan.com 80
Telnet escape character is 'q'.
telnet: www.yuan.com: Name or service not known
www.yuan.com: Unknown host
[root@m01 /server/scripts/devops-shell]# echo $?
1

 

nc -z

[root@m01 /server/scripts/devops-shell]# nc -z www.yuan.com 80
Ncat: Could not resolve hostname "www.yuan.com": Name or service not known. QUITTING.
[root@m01 /server/scripts/devops-shell]# echo $?
2
[root@m01 /server/scripts/devops-shell]# nc -z www.baidu.com 80
[root@m01 /server/scripts/devops-shell]# echo $?
0

 

案例:检查指定的地址是否可以访问

curl

[root@m01 /server/scripts/devops-shell]# cat check_url.sh 
#!/bin/bash
#description: 使用curl命令检查指定的url是否可以访问
. /etc/init.d/functions
#1.vars
url=$1
#2.检查
function check_parameters() {
#2.1检查参数的个数是否为1
if [ $# -ne 1 ];then
  action "请输入一个参数:url" /bin/false
  exit 1
fi
#2.2检查输入的url的格式
[[ $url =~ ^[a-zA-Z0-9:./]+$ ]] || {
  action "输出的url的格式不正确" /bin/false
  exit 2
}
}
#3检查指定的url是否可以访问
function check_url() {
url_code=`curl -s -I $url |awk 'NR==1{print $2}'`
if [ $url_code -lt 400 ];then
  action "$url 可以访问" /bin/true
else 
  action "$url 不可以访问" /bin/false
fi
}

check_parameters $@
check_url

 

wget

[root@m01 /server/scripts/devops-shell]# wget -q --spider www.jd.com
[root@m01 /server/scripts/devops-shell]# echo $?
0
[root@m01 /server/scripts/devops-shell]# wget -q --spider www.yuan.com
[root@m01 /server/scripts/devops-shell]# echo $?
4

循环

循环类型 说明
for循环 最常用的循环
while循环(当型循环) while循环可以加入条件(死循环、读取文件)
do until循环(直到循环) 极少用

for循环

格式

# 格式一:常用格式
for 变量 in 候补清单(列表)
do
    命令
done

# 格式二:C语言格式
for((i=1;i<=10;i++))
do
    命令
done

 

 

案例:使用for循环在/yuan目录下批量创建文件(10个小写随机字符_yuan.html)

[root@m01 /server/scripts/devops-shell]# cat 08.sh 
#!/bin/bash
#1.vars
dir="/yuan"
#2.判断目录是否存在,不存在则创建
if [ -d $dir ];then
  echo "目录 $dir 存在"
else
  echo "目录 $dir 不存在,正在自动创建 $dir "
  mkdir -p $dir
  echo "目录 $dir 创建成功"
fi
#创建文件
for i in {1..10}
do
  filename_suiji=`tr -dc 'a-z' < /dev/urandom|head -c 10`
  filename_full=${filename_suiji}_yuan.html
  touch $dir/$filename_full
done
echo "10个文件已创建"
# tr:用于转换或删除的命令行工具
(读取标准输入,并将其内容经过指定的转换后输出到标准输出)
    # -d:删除字符
        echo "hello world" | tr -d 'lo'  
        # 输出: he wrd  
    # -c:取反输入字符
        echo "hello world" | tr -c 'a-zA-Z' ' '  
        # 输出: hello world(其他字符被替换为空格)  
    /dev/urandom:这个文件是一个伪随机数生成器,可以用来获取随机字节
    tr -dc 'a-z':此命令会过滤从 /dev/urandom 输出的内容
    head -c 10:从 tr 的输出中只取前 10 个字符,作为文件名的一部分

# mkpasswd -l 10 -d 0 -s 0 -c 0
    mkpasswd:Linux系统中用于生成密码的命令
    -l 密码长度
    -d 数字长度
    -s special 特殊字符
    -C 大写字母
    -c 小写字母
    

 

while

格式

while 条件    # while循环只会在满足条件后运行
do
    命令
done

案例:随机输入数字(1-100),判断数字 

[root@m01 /server/scripts/devops-shell]# cat 09.sh 
#!/bin/bash
var=$((RANDOM%100+1))  
  # RANDOM:Bash内置变量,生成一个0~32767的随机整数
  # $((...)) 是 Bash 中进行算术运算的语法
read -p "请输入一个1-100以内的整数:" num

while [[ $num -gt 100 || $num -lt 0 ]]
do
  read -p "请输入一个1-100以内的整数:" num
done

while [[ $num -ne $var ]]
do
  if [ $num -lt $var ];then
    read -p "你输入的数字偏小,请重新输入1-100以内的整数:" num
  else
    read -p "你输入的数字偏大,请重新输入1-100以内的整数:" num
  fi
done
echo "你猜对了数字$num"
while循环-读取文件内容
# 方法1:采用exec读取文件后,进入while循环
exec<FILE
while read line
do
    cmd
    echo $line
done

# 方式2:使用cat读取文件内容,然后通过管道进入 不适用于有变量传递场景使用。
cat FILE|while read line
do
  cmd
  echo $line
done

# 方式3:在while循环结尾done通过输入重定向指定读取的文件(推荐使用) 
while read line
do
  cmd
done<FILE
案例:通过while read方式,统计/etc/passwd文件行数
[root@m01 /server/scripts/devops-shell]# cat 10.sh 
#!/bin/bash
i=0
j=0
dir_file="/etc/passwd"

# 方法二:
cat $dir_file |while read line
do
  let i++
done
echo i=$i

# 方法三:
while read line
do
  let j++
done <$dir_file
echo j=$j
[root@m01 /server/scripts/devops-shell]# sh 10.sh 
i=0  # 管道会在一个子进程中运行 while 循环,所以在这个子进程中修改的变量 i 不会影响到父进程中的变量 i
j=22  # while 循环直接读取文件 dir_file,这样 while 循环是在主进程中执行的

 

 

 

 

 

案例:分析nginx访问日志,找出访问量最高的前3个ip及其访问次数,ip访问次数大于200,通过防火墙屏蔽ip

  • 分析nginx访问日志,访问次数最高的5个ip及其访问次数,保存到/yuan/result.txt
  • 读取result.txt文件,读取ip和次数
  • 判断次数是否大于200并且之前没有屏蔽该ip
[root@m01 /server/scripts/devops-shell]# cat 11.sh 
#!/bin/bash
#1.vars
access_log=/yuan/access.log
result_file=/yuan/result.txt

#2.日志分析
awk '{print $1}' ${access_log} |sort |uniq -c |sort -rn |head -3 > ${result_file}

#3.进行处理
while read line 
do
  ip_count=`echo $line |awk '{print $1}'`
  ip_address=`echo $line |awk '{print $2}'`
  if [ $ip_count -gt 200 -a `iptables -nL |grep -wc "${ip_address}"` -eq 0 ];then
    iptables -t filter -I INPUT -s $ip_address -i DROP
  fi
done <${result_file}

 

do - until 循环

# 直到型循环
until 条件
do
    命令
done

 

循环控制语句

概述

语句 含义
exit 终止整个脚本执行
return 终止函数执行并返回指定的内容
break 终止循环
continue 终止本次循环,执行下一次循环
posted on 2025-04-01 16:54  猿小姜  阅读(13)  评论(0)    收藏  举报

levels of contents