条件表达式
格式
| 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 | 终止本次循环,执行下一次循环 |
浙公网安备 33010602011771号