一、if 的写法
Shell 的 if 和别的语言不太一样,then 是单独拎出来的一段。单分支就是条件成立才干活:
# 单分支(为真就执行代码,为假什么都不做)
if 条件
then
代码
fi
简化
if 条件;then
代码
fi
写在命令行上就是一行的事,注意结尾的 fi 别忘:
ubuntu@VM-0-9-ubuntu:~$ if [ -f "caixuk.jpg" ];then echo "jinitaimei"; fi
嵌套也是可以的:
if <>
then
echo ...
if <>
then
...
fi
fi
if <>
then
echo ...
else
echo...
fi # 记得加fi
多分支用 elif,注意 if 后面要写 then,elif 不用写;嵌套层数尽量别超过三层,套太深自己都看不懂了:
if <>
then # if 写then,elif 不需要
代码1
# elif 嵌套不超3层
elif <>
then
代码2
elif <>
then
代码3
else
代码4
fi # 写 fi
条件测试本身有三种写法,效果一模一样,中括号、双中括号和 test 都行:

if [ -f /etc/hosts ]
then
echo yes
fi
if [[ -f /etc/hosts ]]
then
echo yes
fi
if test -f /etc/hosts
then
echo yes
fi
二、内存检测脚本
先看需求:
- 检测 linux 剩余可用内存,比如小于 100M,给运维发邮件
- 编写crontab,每三分钟检查一次内存
思路就四步:先拿到内存情况,再配邮件告警把剩余内存告诉出去,然后用 if 判断是不是小于阈值,最后交给 crontab 定时跑。
free -m 以 MB 为单位看内存:
ubuntu@VM-0-9-ubuntu:~$ free -m
# 总内存 使用 剩余 缓冲/缓存 可用剩余
total used free shared buff/cache available
Mem: 7623 624 5694 2 1569 6998
Swap: 1987 0 1987
free -m | awk 'NR==2 {print $NF}'
# NF代代表最后一列
awk 的作用就是单独取出一列,而且分得干干净净,取第几列就写第几列:
# 取 access.log 里的 IP 列(第1列)
awk '{print $1}' /var/log/nginx/access.log
# 取前10行
awk '{print $1}' access.log | head -10
# /etc/passwd 是用 : 分隔的,取用户名
awk -F: '{print $1}' /etc/passwd
# CSV 文件,取第2列
awk -F, '{print $2}' file.csv
# 取第 12 行
awk 'NR==12' 文件名
# 取第 1 行(标题行)
awk 'NR==1' 文件名
脚本长这样。需求里写的是 100M,我这份演示机器的内存大,阈值就随手写成了 7000:
#!/bin/bash
# 定义内存和获取提示内存的变量
FreeMem=`free -m | awk 'NR==2 {print $NF}'`
CHARS="现在内存是$FreeMem"
if [ "$FreeMem" -lt "7000" ]
then
echo $CHARS | tee /tmp/messages.txt
# 发邮件需要mail服务
# mail -s 主题 收件人 < 发送信息
# mail -s "`date +%F-%Y`$CHARS" zzz_usr@163.com < /tmp/messages.txt
# 没有配置这个服务,所以先注释掉,改为echo 输出
echo "内存不足7000M"
fi
定时任务 crontab
crontab 的任务必须写进文件里才会执行,写完用 crontab 文件名 加载(文件后缀随便写,用 .cron 一眼能看出是定时任务文件):
分 时 日 月 周 命令
│ │ │ │ │
│ │ │ │ └── 星期几(0-7,0和7都代表周日)
│ │ │ └──── 月份(1-12)
│ │ └────── 日(1-31)
│ └──────── 小时(0-23)
└────────── 分钟(0-59)
ubuntu@VM-0-9-ubuntu:~/shell$ vim mem.cron
ubuntu@VM-0-9-ubuntu:~/shell$ cat mem.cron
*/3 * * * * /home/ubuntu/shell/mem.sh &>/dev/null
ubuntu@VM-0-9-ubuntu:~/shell$ crontab -l
no crontab for ubuntu
ubuntu@VM-0-9-ubuntu:~/shell$ crontab mem.cron
ubuntu@VM-0-9-ubuntu:~/shell$ crontab -l
*/3 * * * * /home/ubuntu/shell/mem.sh &>/dev/null
ubuntu@VM-0-9-ubuntu:~/shell$
五个星号代表“每”,常用的几种:
* * * * * /path/to/script.sh # 每分钟一次
5 * * * * /path/to/script.sh # 每小时的第 5 分钟
0 2 * * * /path/to/script.sh # 每天凌晨 2 点
0 3 * * 1 /path/to/script.sh # 每周一凌晨 3 点
0 4 1 * * /path/to/script.sh # 每月 1 号凌晨 4 点
*/5 * * * * /path/to/script.sh # 每 5 分钟
0 8-18/2 * * * /path/to/script.sh # 上午 8 点到下午 6 点每隔 2 小时
0 2 * * * /path/to/script.sh >> /var/log/script.log 2>&1 # 顺手把输出记进日志
三个容易踩的坑:
*/5不等于“每 5 小时”,*/5 * * * *是每 5 分钟,每 5 小时要写成0 */5 * * *- 周日是 0 或 7,两个都行,
0 3 * * 0和0 3 * * 7都是每周日 3 点 - 日和周同时限制时容易出问题,
0 0 1 * 1是每月 1 号且周一(很少同时满足),只想“每月 1 号”或者“每周一”就分开写成两行
三、怎么判断服务还活着
先拿两个数字练下手,脚本接收传进来的两个参数,用 -lt、-eq、-gt 比大小:
#!/bin/bash
# 接收传入的两个参数
a=$1
b=$2
if [ "$a" -lt "$b" ]
then
echo " $a < $b "
exit 0
fi
if [ "$a" -eq "$b" ]
then
echo " $a = $b "
exit 0
fi
if [ "$a" -gt "$b" ]
then
echo " $a > $b "
exit 0
fi
ubuntu@VM-0-9-ubuntu:~/shell$ bash ab.sh 3 4
3 < 4
同样的意思写成 if-elif-else 更清爽:
#!/bin/bash
# 接收传入的两个参数
a=$1
b=$2
if [ "$a" -lt "$b" ]
then
echo "$a less than $b"
elif [ "$a" -eq "$b" ]
then
echo "$a equal to $b"
else
echo "$a greater than $b"
fi
ubuntu@VM-0-9-ubuntu:~/shell$ bash ab.sh 3 4
3 less than 4
判断服务状态其实分好几层,从本机看到远端探、从端口看到进程看到业务,当时梳理的角度大概是这些:

本机看端口常用 netstat、ss、lsof;从远端探本机端口用 telnet、nmap、nc;看进程数就 ps -ef | grep 配合 wc -l;再往上一层就是真的去登录数据库或者访问接口。
netstat 的参数:
| 参数 | 含义 |
|---|---|
-t | TCP 连接 |
-u | UDP 连接 |
-n | 显示 IP 和端口号(不解析域名) |
-l | 只显示正在监听的端口 |
-p | 显示占用端口的进程名/PID |
先把 mysql 装上,设置好 root 密码:
sudo apt install mysql-server -y
# 设置密码
# 进入 mysql 环境
mysql
# 设置密码(用的是root的密码)
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '密码';
FLUSH PRIVILEGES;
# exit 退出
# 测试连接
# 参数 -p 和密码是无空格的,后面不写密码就是交互式输入密码,更安全
mysql -u root -p密码
- 看端口
# 查看端口
root@VM-0-9-ubuntu:~# netstat -tunlp|grep mysql
tcp 0 0 127.0.0.1:33060 0.0.0.0:* LISTEN 125623/mysqld
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN 125623/mysqld
# 但我们要用端口存活表示mysql还在,这一坨需要优化
root@VM-0-9-ubuntu:~# netstat -tunlp|grep mysql| wc -l
2
# 如果输出2,代表mysql存活
# 使用 ss 命令
root@VM-0-9-ubuntu:~# ss -tunlp|grep mysql| wc -l
2
# 使用 lsof -i tcp:3306 查看3306被谁占用了
root@VM-0-9-ubuntu:~# lsof -i tcp:3306
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
mysqld 125623 mysql 23u IPv4 21041127 0t0 TCP localhost:mysql (LISTEN)
# 上面的命令,只要输出不为 0,mysql 服务就存活
netstat 和 ss 其实是一家人,现在的新系统更推荐 ss,netstat 要装 net-tools 才有。
- 用 nmap 扫
# 安装 nmap 端口扫描软件
# centos的
yum install -y telnet nmap nc
# ubuntu的
apt install -y telnet nmap netcat-openbsd
# 扫
root@VM-0-9-ubuntu:~# nmap 127.0.0.1 -p 3306
Starting Nmap 7.94SVN ( https://nmap.org ) at 2026-07-24 21:04 CST
Nmap scan report for localhost (127.0.0.1)
Host is up (0.000047s latency).
# 下面是内容
PORT STATE SERVICE
3306/tcp open mysql
Nmap done: 1 IP address (1 host up) scanned in 0.04 seconds
# 也可以主动过滤开放的端口
root@VM-0-9-ubuntu:~# nmap 127.0.0.1 -p 3306 | grep open
3306/tcp open mysql
# 这个命令配合wc输出不为 0 就是存活,否则挂掉
- 用 telnet 连一下
# 用 echo 模拟一次回车输入,然后通过管道传给 telnet
# 让 telnet 自动连接本机的 3306 端口,并立即退出
root@VM-0-9-ubuntu:~# echo -e "\n" | telnet 127.0.0.1 3306
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
[
8.0.46-0ubuntu0.24.04.3
WTcV#ÿirTAfB]+caching_sha2_passwordConnection closed by foreign host.
# 判断connected信息,有信息就是连上了(这里默认mysql是3306)
# 注意Connected是大写的 C,linux严格匹配大小写
root@VM-0-9-ubuntu:~# echo -e "\n" | telnet 127.0.0.1 3306 | grep Connect | wc -l
Connection closed by foreign host.
1
- 看进程数
root@VM-0-9-ubuntu:~# ps -ef | grep mysql
mysql 125623 1 0 20:49 ? 00:00:03 /usr/sbin/mysqld
root 132892 124675 0 21:18 pts/3 00:00:00 grep --color=auto mysql
# 这里有一个 grep 本身产生的进程,可以去掉
# -v 表示反向匹配(invert-match)
ps -ef | grep mysql | grep -v grep
root@VM-0-9-ubuntu:~# ps -ef | grep mysql | grep -v grep | wc -l
1
四、用 php 和 python 连一下 mysql
端口活着不代表数据库能用,最直接的办法是拿业务代码去连一下,能读到数据就说明没问题。这里准备了 php 和 py 两个程序。
php 连 mysql
先装 mysql,再准备 php 环境的依赖:
centos yum remove -y php-mysql、yum install -y php-mysqlnd php
ubuntu apt remove -y php-mysql、apt install -y php-mysql php
然后写连接代码:
<?php
$mysql_id=mysql_connect("localhost","root","密码")
# mysql_connect() 如果连接成功,会返回一个 MySQL 连接标识符(资源类型)
# 这个值在条件判断中会被视为 true;如果连接失败,会返回 false
if ($mysql_id) {
echo "mysql服务运行中"
}else {
# 打印报错函数信息
echo mysql_error();
}
这个脚本在 PHP 7.0+ 环境下会直接报错,因为 mysql_connect() 和 mysql_error() 已经被彻底移除了,所以真要用得换成 mysqli 的写法,脚本存成 mysql_server.php:
<?php
$mysql_id = mysqli_connect("localhost", "root", "密码");
if ($mysql_id) {
echo "mysql服务运行中";
} else {
echo mysqli_connect_error();
}
跑一下:
root@VM-0-9-ubuntu:~/shell# php mysql_server.php
mysql服务运行中root@VM-0-9-ubuntu:~/shell#
python 连 mysql
先装 python3 的环境依赖:
centos yum install -y python3-devel python3-pip
ubuntu apt install -y python3-dev python3-pip
再用包管理工具装连接 mysql 的模块:
pip3 install pymysql
# Ubuntu 24.04+ 的新保护机制,禁止用系统 pip 装包,怕搞乱系统环境
error: externally-managed-environment
# 但是 pip3 已经是系统自带的了
root@VM-0-9-ubuntu:~/shell# which pip3
/usr/bin/pip3
py 代码数量要严格控制,就这么几行:
import pymysql
# 连接数据库
db = pymysql.connect(
host="localhost",
user="root",
password="密码",
db="mysql",
charset="utf8" # 注意这里不是utf-8没有段横杠
)
# 操作数据库
cursor=db.cursor()
cursor.execute("select version()")
data=cursor.fetchone()
print(f"数据库已连接,版本:{data[0]}")
# 或
print("数据库已连接,版本:%s" % data[0])
db.close()
实际跑的时候先报了个缺模块的错,加 --break-system-packages 装上 pymysql 就好了:
root@VM-0-9-ubuntu:~/shell# cat py3_mysql.py
import pymysql
# 连接数据库
db = pymysql.connect(
host="localhost",
user="root",
password="xxxxx",
db="mysql",
charset="utf8"
)
# 操作数据库
cursor=db.cursor()
cursor.execute("select version()")
data=cursor.fetchone()
print(f"数据库已连接,版本:{data[0]}")
db.close()
# 发现报错,少了安装环境
root@VM-0-9-ubuntu:~/shell# python3 py3_mysql.py
Traceback (most recent call last):
File "/root/shell/py3_mysql.py", line 1, in <module>
import pymysql
ModuleNotFoundError: No module named 'pymysql'
root@VM-0-9-ubuntu:~/shell# pip3 list | grep pymysql
root@VM-0-9-ubuntu:~/shell# pip3 install --break-system-packages pymysql
root@VM-0-9-ubuntu:~/shell# python3 py3_mysql.py
数据库已连接,版本:8.0.46-0ubuntu0.24.04.3
把服务停掉,立马就报错了:
root@VM-0-9-ubuntu:~/shell# systemctl stop mysql
root@VM-0-9-ubuntu:~/shell# python3 py3_mysql.py
Traceback (most recent call last):
File "/usr/local/lib/python3.12/dist-packages/pymysql/connections.py", line 682, in connect
sock = socket.create_connection(
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/socket.py", line 852, in create_connection
raise exceptions[0]
File "/usr/lib/python3.12/socket.py", line 837, in create_connection
sock.connect(sa)
ConnectionRefusedError: [Errno 111] Connection refused
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/root/shell/py3_mysql.py", line 3, in <module>
db = pymysql.connect(
^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/pymysql/connections.py", line 372, in __init__
self.connect()
File "/usr/local/lib/python3.12/dist-packages/pymysql/connections.py", line 744, in connect
raise exc
pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on 'localhost' ([Errno 111] Connection refused)")
pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on 'localhost' ([Errno 111] Connection refused)") 这句就是连不上数据库地址的意思。
五、把几种判断凑成一个脚本
前面那些命令一条条敲太累,写进 test_mysql.sh 里,五种办法轮流上一遍:
root@VM-0-9-ubuntu:~/shell# cat test_mysql.sh
#! /bin/bash
echo "---method 1---"
# 返回结果不为0,只能是大于0
if [ `netstat -tunlp | grep mysql |wc -l` -gt "0" ]
then
echo "mysql is running"
else
echo "mysql is stopped..."
systemctl enable --now mysql
fi
echo "---method 2---"
if [ `ss -tunlp | grep mysql |wc -l` -gt "0" ]
then
echo "mysql is running"
else
echo "mysql is stopped..."
systemctl enable --now mysql
fi
echo "---method 3---"
if [ `lsof -i tcp:3306 | grep mysql |wc -l` -gt "0" ]
then
echo "mysql is running"
else
echo "mysql is stopped..."
systemctl enable --now mysql
fi
# 使用php脚本
# 执行脚本根据返回值判断是否存活
echo "---method 4---"
php /root/shell/mysql_server.php
if [ $? -eq 0 ]
then
echo "mysql is running"
else
echo "mysql is stopped..."
systemctl enable --now mysql
fi
# 使用py脚本
# 现在 Method 4 和 5 的写法对了,但 Method 5 有一个隐患
# $? 取的是 python3 的返回值
# 但中间隔了一个 echo "---method 5---",导致 $? 被覆盖
# 先保存返回值即可(推荐),把 echo 放到 if 里面也可以
python3 /root/shell/py3_mysql.py
PYTHON_EXIT=$?
echo "---method 5---"
if [ $PYTHON_EXIT -eq 0 ]; then
echo "mysql is running"
else
echo "mysql is stopped..."
systemctl enable --now mysql
fi
# 修改文件太多,不想删除可以使用 EOF 定界符
cat > /root/shell/test_mysql.sh <<'EOF'
内容
EOF
五种办法都通过:
# 输出结果
root@VM-0-9-ubuntu:~/shell# bash test_mysql.sh
---method 1---
mysql is running
---method 2---
mysql is running
---method 3---
mysql is running
---method 4---
mysql服务运行中mysql is running
---method 5---
数据库已连接,版本:8.0.46-0ubuntu0.24.04.3
mysql is running