shell 脚本知识点总结

Shell 脚本知识点总结

适用:Linux bash,运维面试、写自动化脚本、日志处理、批量操作

一、基础概念

  1. 脚本开头

    #!/bin/bash

    告诉系统用bash解释执行,必须写在第一行

    注释:# 单行注释;没有原生多行注释,可使用 :<<EOF ... EOF 模拟多行注释

  2. 执行方式

    方式1(推荐,新开子shell)

    bash test.sh
    sh test.sh

    方式2(需要执行权限)

    chmod +x test.sh
    ./test.sh

    方式3(当前shell执行,不新建子shell,变量会留在当前环境)

    source test.sh
    . test.sh

重点:source 在当前 shell 运行;./xxx.sh 开启子 shell,脚本内变量执行完自动销毁。

二、变量

1. 普通变量

复制代码
name="zhangsan"   # =两边**不能有空格**
echo $name
echo ${name}      # 推荐大括号,变量和后面字符区分开
  • 字符串有空格必须加引号:msg="hello world"
  • 单引号 ' ':原样输出,不解析变量;双引号 " ":解析变量、转义字符

2. 特殊位置变量(脚本传参)

表格

变量 含义
$0 脚本本身名称
$1 $2 ... $n 第 1、2、n 个参数
$# 传入参数总个数
$* 所有参数,当成1 个字符串(被双引号包裹时)
$@ 所有参数,独立多个字符串(循环推荐用 $@)
$? 上一条命令返回值,0 = 成功,非 0 = 失败
$$ 当前脚本 PID
$! 后台运行的最后一个进程 PID

面试高频:$* 和 $@ 区别,双引号下差异最大。

3. 只读变量 & 删除变量

复制代码
readonly age=20  # 只读,不能修改
unset name       # 删除变量,不能删只读变量

三、运算符

1. 算术运算(bash 只支持整数)

复制代码
# 写法1 $(( )) 【最推荐】
a=$((1+2))
b=$((a*3))

# 写法2 expr 注意运算符两边空格
c=`expr 1 + 2`

⚠️ bash 不支持小数运算 ,小数要用 bc 工具:

复制代码
echo "scale=2; 10/3" | bc

2. 比较运算符([ ] 测试,两边必须空格)

数字比较

表格

符号 含义
-eq 等于 equal
-ne 不等于 not equal
-gt 大于 greater than
-lt 小于 less than
-ge 大于等于
-le 小于等于
复制代码
if [ $a -gt 10 ];then echo ">10";fi
字符串比较

表格

符号 含义
= 相等
!= 不等
-z str 字符串长度为 0(空)
-n str 字符串非空
复制代码
[ "$name" = "root" ]

字符串变量建议双引号包裹,防止变量为空时报语法错误。

文件测试(高频!运维脚本)

表格

参数 作用
-f 是否存在且是普通文件
-d 是否存在且是目录
-e 文件 / 目录是否存在
-r 可读
-w 可写
-x 可执行
-s 文件存在且大小 > 0(非空文件)
复制代码
if [ -f "/etc/passwd" ];then echo "文件存在";fi

3. 逻辑运算符

复制代码
[ 条件1 ] && [ 条件2 ]   # 与
[ 条件1 ] || [ 条件2 ]   # 或
! [ 条件 ]               # 非

双中括号 [[ ]] 是 bash 扩展,支持 && ||,不用分开写,更稳定:

复制代码
[[ $a -gt 5 && $a -lt 10 ]]

四、流程控制

1. if 判断

复制代码
if [ 条件 ];then
  命令
elif [ 条件 ];then
  命令
else
  命令
fi

2. case 分支(匹配字符串)

复制代码
case $var in
"start")
  echo "启动服务"
;;
"stop")
  echo "停止服务"
;;
*)
  echo "未知参数"
;;
esac

3. for 循环

复制代码
# 方式1 遍历列表
for i in $@
do
  echo $i
done

# 方式2 C风格for(bash)
for ((i=0;i<10;i++))
do
  echo $i
done

4. while 循环

复制代码
# 读取文件经典写法
while read line
do
  echo $line
done < test.txt

# 数字循环
i=1
while [ $i -le 5 ]
do
  echo $i
  i=$((i+1))
done

until:条件不成立时循环,很少用。

五、函数

复制代码
# 定义,必须先定义再调用
func(){
  echo "函数"
  echo $1  # 函数内部参数,独立于脚本参数
  return 0 # 返回值只能0~255整数
}

func hello  # 调用,传参
echo $?     # 获取函数return返回值

函数返回值只能是状态码;想要返回字符串,用 echo 输出,变量捕获:

复制代码
res=$(func)

六、字符串处理

复制代码
str="abcdef"
# 获取长度
${#str}

# 切片:${变量:起始位置:长度},下标从0开始
${str:1:3} # bcd

# 替换
${str/a/x} # 替换第一个a为x
${str//a/x} # 替换全部a为x

# 删除
${str#ab}  # 从开头最短删除ab
${str%ef}  # 从结尾最短删除ef

七、数组

复制代码
# 一维数组
arr=("apple" "banana" "orange")
echo ${arr[0]}       # 取第0个元素
echo ${arr[@]}       # 全部元素
echo ${#arr[@]}      # 数组长度
arr[2]="grape"       # 修改元素

bash 只有一维数组,不支持二维数组。

八、输入输出

  1. read 接收控制台输入

    read -p "请输入名字:" name
    read -s passwd # -s 不回显(密码)
    read -t 5 var # 5秒超时

  2. 重定向

    覆盖输出

    追加输出
    < 输入重定向
    2> 错误输出重定向
    &> 标准输出+错误输出全部重定向

  3. 管道 |:前命令 stdout 作为后命令 stdin

    ps aux | grep nginx

九、常用内置命令 & 工具

  • seq:生成序列 seq 1 10
  • cut:截取列,cut -d: -f1 /etc/passwd
  • awk:文本分析(shell 脚本三剑客最强)
  • sed:流编辑,替换文本 sed 's/old/new/g' file
  • grep:过滤文本

三剑客:grep (过滤) → sed (增删改) → awk (统计、格式化输出)

十、脚本调试

复制代码
# 逐行打印执行过程,调试
bash -x test.sh
# 只检查语法不执行
bash -n test.sh
# 脚本内开启调试
set -x  # 开启
set +x  # 关闭

十一、常见坑(面试重点)

  1. = 赋值两边不能有空格
  2. [ ] 条件前后必须留空格
  3. 变量引用尽量 "$var" 双引号,防止空格、空值炸脚本
  4. $? 只能保存上一条命令状态,中间不能插入其他命令
  5. bash 算术只支持整数,小数计算用 bc
  6. $* 和 $@ 在双引号下行为不一样
  7. 子 shell 内修改变量,不会影响父 shell(管道会创建子 shell)
  8. 函数 return 只能返回 0~255,不能返回字符串

十二、面试简答速记版

  1. shell 脚本开头 #!/bin/bash
  2. 执行方式:./需要权限;source在当前 shell 执行
  3. $1 $# $? $@ 位置参数含义
  4. 数字比较 -eq/-gt,不能直接用 > <(>在 \[\] 里是重定向)
  5. 文件判断 -f -d -e
  6. [[ ]] 比 [ ] 更强大,支持 && || 正则
  7. for、while 循环,读取文件 while read 经典写法
  8. 函数定义、传参、return 限制
  9. bash -x 调试脚本
  10. 三剑客:grep sed awk

===================================

常见shell脚本

1.mysql备份脚本

我写过生产环境的 MySQL 定时备份脚本,用 mysqldump 配合 gzip 压缩,crontab 每天凌晨执行,find -mtime 清理7天前的旧备份。上线时踩过 cron 环境变量导致 mysqldump 找不到的坑,改成绝对路径

复制代码
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
DB_USER="root"
DB_PASS="123456"
DB_NAME="testdb"
BACKUP_DIR="/data/backup"

# 备份并压缩
mysqldump -u${DB_USER} -p${DB_PASS} ${DB_NAME} | gzip >${BACKUP_DIR}/${DB_NAME}_${DATE}.sql.gz

# 删除7天前的旧备份
find ${BACKUP_DIR} -name "*.sql.gz" -mtime +7 -exec rm -f {} \;

2.服务器主机状态监控脚本

编写 Shell 脚本定时采集 CPU、内存、磁盘使用率和服务进程状态,超过阈值时记录日志并发送告警;结合 crontab 实现无人值守运行,减少人工巡检次数。

复制代码
#!/bin/bash

CPU_THRESHOLD=0.1
MEM_THRESHOLD=2
DISK_THRESHOLD=2
LOG="/var/log/monitor.log"
# 每次执行先清空旧日志,防止重复发送历史告警
> ${LOG}

# 获取cpu使用率
CPU_USAGE=$(top -b -n2 | grep Cpu | awk '{print 100 - $8}' | tail -n1)
# 获取内存使用率
MEM_USAGE=$(free | awk '/Mem/{printf("%.0f", ($2-$7)/$2*100)}')
# 获取根分区磁盘使用率
DISK_USAGE=$(df -h / | awk 'NR==2{print $5}' | cut -d'%' -f1)

# 触发告警时,同时echo打印 + 写入日志
if (( $(echo "$CPU_USAGE > $CPU_THRESHOLD" | bc -l) ));then
    MSG="CPU告警:当前使用率${CPU_USAGE}%"
    echo "${MSG}"
    echo "${MSG}" >> ${LOG}
fi
if (( $(echo "$MEM_USAGE > $MEM_THRESHOLD" | bc -l) ));then
    MSG="内存告警:当前使用率${MEM_USAGE}%"
    echo "${MSG}"
    echo "${MSG}" >> ${LOG}
fi
if (( $(echo "$DISK_USAGE > $DISK_THRESHOLD" | bc -l) ));then
    MSG="磁盘告警:当前使用率${DISK_USAGE}%"
    echo "${MSG}"
    echo "${MSG}" >> ${LOG}
fi

# ===================== 钉钉机器人推送部分 =====================
# [ -s ${LOG} ] 判断:文件存在并且文件大小>0(里面有告警内容)
if [ -s ${LOG} ];then
curl -s -X POST "https://oapi.dingtalk.com/robot/send?access_token=fc9cf6ed0fa6a27d94d3fdbe65b648d89d933ed9c143f8fa6c87767bfc2aba3e" \
    -H 'Content-Type: application/json' \
    -d "{
        \"msgtype\":\"text\",
        \"text\":{
            \"content\":\"服务器资源告警:\n$(cat ${LOG})\"
        }
    }"
fi

3.Nginx 访问日志分析脚本

使用 awk、sort、uniq 等命令统计访问日志中的 Top IP、状态码分布和异常请求,辅助快速定位访问异常。

复制代码
#!/bin/bash
# Nginx 访问日志分析脚本
LOG_FILE="${1:-/var/log/nginx/access.log}"

[ -f "$LOG_FILE" ] || { echo "错误:日志文件不存在 $LOG_FILE" >&2; exit 1; }
[ -s "$LOG_FILE" ] || { echo "日志为空,无数据可分析"; exit 0; }   # 空文件防御

echo "=========================================="
echo "Nginx 访问日志分析报告    $(date '+%F %T')"
echo "日志文件:$LOG_FILE"
echo "=========================================="

echo -e "\n【1】PV/UV"
printf "PV: %s   UV: %s\n" "$(wc -l < "$LOG_FILE")" \
       "$(awk '{print $1}' "$LOG_FILE" | sort -u | wc -l)"

echo -e "\n【2】总流量"
awk '{sum+=$10} END{printf "%.2f MB\n", sum/1024/1024}' "$LOG_FILE"

echo -e "\n【3】访问 Top10 IP"
awk '{c[$1]++} END{for(i in c) print c[i], i}' "$LOG_FILE" | sort -rn | head -10

echo -e "\n【4】状态码分布"
awk '{c[$9]++} END{for(i in c) print c[i], i}' "$LOG_FILE" | sort -rn

echo -e "\n【5】异常请求(4xx/5xx)分布"
awk '$9 ~ /^[45]/ {c[$9]++} END{for(i in c) print c[i], i}' "$LOG_FILE" | sort -rn

echo -e "\n【6】404 来源 Top5(疑似扫描)"
awk '$9==404 {c[$1]++} END{for(i in c) print c[i], i}' "$LOG_FILE" | sort -rn | head -5

echo -e "\n【7】热门 URL Top10"
awk '{c[$7]++} END{for(i in c) print c[i], i}' "$LOG_FILE" | sort -rn | head -10

echo -e "\n【8】每小时请求分布"
awk '{h=substr($4,14,2); c[h]++} END{for(i in c) print i":00", c[i]}' "$LOG_FILE" | sort -n

echo -e "\n【9】User-Agent Top5"
awk -F'"' '{c[$6]++} END{for(i in c) print c[i], i}' "$LOG_FILE" | sort -rn | head -5
相关推荐
程序员Sunday1 小时前
Flex 和 Grid 有什么区别?一维布局、二维布局怎么选?
前端·css·面试
Jlzn88881 小时前
华南PACK线自动化率怎么统计才靠谱
前端·javascript·自动化
拖孩1 小时前
AI 时代,个人开发者到底能不能靠一个小程序赚到钱
前端·后端·微信小程序
Hilaku1 小时前
ERP选型时不问MCP,等于默认三年后再付一次集成费!
前端·javascript·程序员
程序人生8881 小时前
几百张固定位置扫描件如何离线批量清?DocConverter Web 批量去水印链路
前端
deli0071 小时前
一个汉字三种编码:UTF-8、GBK、UTF-16 逐字节对比实测
前端
庖丁解牛1 小时前
第二课:把代码保存成网页 + 认识变量 (2)
前端
代码猎人2 小时前
怎么开发一个Vue3项目
前端
梦里寻码2 小时前
从 0 到 1 用 NestJS + LangChain 搭一套可售卖的 SaaS AI 客服:挂件一行代码接入,知识库秒变 SEO 帮助中心
前端·后端·github