Linux常用命令与Shell脚本实战:从手动敲命令到自动化运维
还在一个个手动敲命令?学会Shell脚本,把重复劳动一键搞定
这项技术到底解决了什么问题?
无论你是开发、测试还是运维,工作中总有一些事情需要登录Linux服务器去做:查看日志、检查磁盘空间、重启服务、批量修改文件、定时备份数据库。如果你每次都手动敲命令,不仅效率低,还容易出错——尤其是在凌晨三点被报警叫起来的时候。
Shell脚本是Linux运维的“自动化开关”。把一系列命令写进一个文件,加上执行权限,就变成了一个可重复运行、不会出错的工具。
本教程将从最常用的Linux命令开始,循序渐进到Shell脚本编程,最后给出几个可以直接用于生产环境的实用脚本。
环境准备
本教程可以在以下任一环境中练习:
- • macOS终端(自带bash/zsh,大部分命令通用)
- • Docker容器(快速启动Linux环境,不怕搞坏):
docker run -it --name linux-practice ubuntu:22.04 bash# 安装常用工具apt update && apt install -y vim curl wget procps net-tools iputils-ping
以下所有命令在Ubuntu/Debian系Linux上验证通过。
第一步:文件与目录操作(每天用100遍)
# ============================================# 目录导航# ============================================pwd# 显示当前目录cd /var/log # 切换到指定目录cd .. # 返回上级目录cd - # 返回上一次所在的目录cd ~ # 返回home目录# ============================================# 查看文件# ============================================ls -la # 列出所有文件(含隐藏文件),详细信息ls -lh # 人性化显示文件大小(KB/MB/GB)ls -lt # 按修改时间排序tree -L 2 # 树状显示目录结构(限制2层)cat file.txt # 查看整个文件head -20 file.txt # 查看前20行tail -20 file.txt # 查看最后20行tail -f app.log # 实时追踪日志(Ctrl+C退出)less file.txt # 分页查看(空格翻页,q退出,/搜索)# ============================================# 文件操作# ============================================cp source.txt dest.txt # 复制文件cp -r source_dir/ dest_dir/ # 递归复制目录mv old.txt new.txt # 移动/重命名rm file.txt # 删除文件rm -rf dir/ # 递归强制删除目录(慎用!)mkdir -p a/b/c # 递归创建目录touch newfile.txt # 创建空文件或更新修改时间# ============================================# 文件权限# ============================================chmod +x script.sh # 添加执行权限chmod 755 script.sh # 数字方式:rwxr-xr-xchown user:group file.txt # 修改所有者ls -l file.txt # 查看文件权限
第二步:文本处理三剑客(grep、sed、awk)
这三个命令是Linux文本处理的灵魂。掌握它们,你就能在命令行中快速分析和处理任何文本。
grep —— 文本搜索
# 基本搜索grep "ERROR" app.log # 查找包含ERROR的行grep -i "error" app.log # 忽略大小写grep -v "DEBUG" app.log # 排除包含DEBUG的行grep -c "ERROR" app.log # 统计匹配行数grep -A 3 "ERROR" app.log # 显示匹配行及后面3行grep -B 3 "ERROR" app.log # 显示匹配行及前面3行grep -n "ERROR" app.log # 显示行号# 正则搜索grep -E "ERROR|FATAL" app.log # 搜索ERROR或FATALgrep -E "^2026-01-15" app.log # 搜索指定日期grep -E "[0-9]{3,}" app.log # 搜索包含3位以上数字的行# 递归搜索grep -r "TODO" src/ # 递归搜索目录下所有文件grep -rl "TODO" src/ # 只显示文件名
sed —— 流编辑器(文本替换)
# 替换(最常用)sed 's/old/new/' file.txt # 每行替换第一个匹配sed 's/old/new/g' file.txt # 每行替换所有匹配sed -i 's/old/new/g' file.txt # 直接修改文件(-i)sed -i.bak 's/old/new/g' file.txt # 修改前备份为.bak# 删除行sed '1d' file.txt # 删除第1行sed '1,10d' file.txt # 删除第1到10行sed '/ERROR/d' file.txt # 删除包含ERROR的行# 插入和追加sed '1i\新行' file.txt # 在第1行之前插入sed '1a\新行' file.txt # 在第1行之后追加
awk —— 文本分析
# 按列操作(默认以空格/Tab分隔)awk '{print $1}' file.txt # 打印第1列awk '{print $1, $3}' file.txt # 打印第1列和第3列awk '{print $NF}' file.txt # 打印最后一列awk -F':''{print $1}' /etc/passwd # 以:为分隔符# 条件过滤awk '$3 > 100' data.txt # 第3列大于100的行awk '/ERROR/' app.log # 包含ERROR的行awk '$3 > 100 {print $1, $3}' data.txt # 第3列>100时打印第1和第3列# 统计awk '{sum+=$3} END {print sum}' data.txt # 第3列求和awk '{count++} END {print count}' data.txt # 统计行数awk '{arr[$1]++} END {for(k in arr) print k, arr[k]}' data.txt # 按第1列分组统计# 实用示例:分析Nginx日志awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10# 统计访问量TOP 10的IP
第三步:系统监控与进程管理
# ============================================# 磁盘和内存# ============================================df -h # 查看磁盘使用情况(-h人性化显示)du -sh /var/log/* # 查看目录/文件大小free -h # 查看内存使用情况# ============================================# 进程管理# ============================================ps aux # 查看所有进程ps aux | grep nginx # 查找指定进程top # 实时进程监控(按q退出)htop # 更友好的top(需要安装)kill 1234 # 终止进程(PID=1234)kill -9 1234 # 强制终止pkill -f nginx # 按名称终止进程# ============================================# 网络# ============================================netstat -tlnp # 查看监听的端口ss -tlnp # 更现代的netstat替代lsof -i :8080 # 查看哪个进程占用了8080端口curl -I https://example.com # 查看响应头wget https://example.com/file.tar.gz # 下载文件# ============================================# 查看日志# ============================================journalctl -u nginx -f # 实时查看nginx服务日志dmesg | tail -20 # 查看内核日志
第四步:管道、重定向与组合命令
# ============================================# 管道 | :将前一个命令的输出作为后一个命令的输入# ============================================cat app.log | grep ERROR | wc -l# 解释:读取日志 → 过滤ERROR → 统计行数# 实战示例ps aux | grep python | awk '{print $2}' | xargs kill# 解释:找到所有python进程 → 提取PID → 全部终止# ============================================# 重定向:> 覆盖写入,>> 追加写入# ============================================echo"日志开始" > app.log # 覆盖写入echo"新的一行" >> app.log # 追加写入# 实战示例:备份 + 压缩tar -czf backup.tar.gz /data/ # 打包压缩mysqldump -u root -p dbname > backup.sql # 数据库导出# ============================================# 错误处理:2>&1 将错误输出也重定向# ============================================./script.sh > output.log 2>&1 # 标准输出和错误都写入文件./script.sh > /dev/null 2>&1 # 丢弃所有输出(静默执行)
第五步:Shell脚本编程基础
创建一个简单的Shell脚本,然后逐步添加功能。
脚本1:Hello World
创建 hello.sh:
#!/bin/bash# 第一行必须是shebang,指定解释器echo"Hello, World!"echo"当前时间: $(date)"echo"当前用户: $(whoami)"echo"当前目录: $(pwd)"
运行:
chmod +x hello.sh./hello.sh
脚本2:变量与输入
创建 backup.sh:
#!/bin/bash# ============================================# 变量定义# ============================================SOURCE_DIR="/var/log"BACKUP_DIR="/tmp/backups"DATE=$(date +%Y%m%d_%H%M%S) # 命令替换BACKUP_FILE="$BACKUP_DIR/backup_$DATE.tar.gz"# ============================================# 用户输入($1是第一个参数,$2是第二个...)# ============================================if [ -n "$1" ]; then# -n 检查字符串非空 SOURCE_DIR="$1"fiecho"源目录: $SOURCE_DIR"echo"备份文件: $BACKUP_FILE"# ============================================# 条件判断# ============================================if [ ! -d "$SOURCE_DIR" ]; then# -d 检查目录是否存在echo"错误: 源目录 $SOURCE_DIR 不存在!"exit 1 # 非0退出码表示失败fimkdir -p "$BACKUP_DIR"# 执行备份tar -czf "$BACKUP_FILE""$SOURCE_DIR" 2>/dev/nullif [ $? -eq 0 ]; then# $? 上一条命令的退出码echo"✅ 备份成功: $BACKUP_FILE"echo" 大小: $(du -sh $BACKUP_FILE | cut -f1)"elseecho"❌ 备份失败!"exit 1fi
条件判断速查表:
| | |
-f file | -z str | -eq |
-d dir | -n str | -ne |
-x file | str1 = str2 | -gt |
-r file | str1 != str2 | -lt |
脚本3:循环与函数
创建 batch_process.sh:
#!/bin/bash# ============================================# 函数定义# ============================================process_file() {local file="$1"# local声明局部变量echo"处理文件: $file"# 统计行数 lines=$(wc -l < "$file")echo" 行数: $lines"# 查找ERROR errors=$(grep -c "ERROR""$file" 2>/dev/null || echo 0)if [ "$errors" -gt 0 ]; thenecho" ⚠️ 发现 $errors 个ERROR"fi}# ============================================# for 循环:遍历列表# ============================================echo"=== 遍历文件列表 ==="for file in /var/log/*.log; doif [ -f "$file" ]; then process_file "$file"fidone# ============================================# while 循环:监控进程# ============================================echo""echo"=== 监控nginx进程(最多10秒)==="count=0while [ $count -lt 10 ]; doif pgrep -f nginx > /dev/null; thenecho"nginx 正在运行"elseecho"nginx 未运行!"fisleep 1 ((count++)) # 自增done# ============================================# 遍历命令输出# ============================================echo""echo"=== 当前目录下的大文件(>10MB)==="find . -type f -size +10M | whileread -r file; doecho"$(du -sh "$file" | cut -f1)$file"done
脚本4:交互式菜单
创建 menu.sh:
#!/bin/bashshow_menu() {echo""echo"=========================="echo" 系统管理菜单"echo"=========================="echo"1) 查看磁盘使用"echo"2) 查看内存使用"echo"3) 查看CPU负载"echo"4) 查看网络连接"echo"5) 查看最近登录"echo"0) 退出"echo"=========================="}whiletrue; do show_menuread -p "请选择 [0-5]: " choicecase$choicein 1) df -h ;; 2) free -h ;; 3) uptime ;; 4) ss -tlnp ;; 5) last -n 5 ;; 0) echo"再见!"; exit 0 ;; *) echo"无效选择,请输入0-5" ;;esacread -p "按回车键继续..."done
第六步:生产环境实用脚本
脚本A:日志清理(保留最近7天)
#!/bin/bash# 文件名: cleanup_logs.sh# 用法: ./cleanup_logs.sh /var/log/appLOG_DIR="${1:-/var/log/app}"RETENTION_DAYS=7if [ ! -d "$LOG_DIR" ]; thenecho"目录不存在: $LOG_DIR"exit 1fiecho"清理 $LOG_DIR 中超过 $RETENTION_DAYS 天的日志..."# 查找并删除deleted_count=0deleted_size=0find "$LOG_DIR" -name "*.log" -mtime +$RETENTION_DAYS | whileread -r file; do size=$(stat -c%s "$file" 2>/dev/null || echo 0)echo" 删除: $file ($(numfmt --to=iec $size))"rm -f "$file"doneecho"✅ 清理完成"
脚本B:服务健康检查
#!/bin/bash# 文件名: health_check.sh# 用法: ./health_check.shTARGET="${1:-http://localhost:8080/health}"TIMEOUT=5MAX_RETRIES=3check_health() {for i in $(seq 1 $MAX_RETRIES); do status=$(curl -s -o /dev/null -w "%{http_code}" \ --connect-timeout $TIMEOUT \ --max-time $TIMEOUT \"$TARGET" 2>/dev/null)if [ "$status" = "200" ]; thenecho"✅ 健康检查通过 ($TARGET)"return 0fiecho"⚠️ 第${i}次检查失败 (HTTP $status),等待重试..."sleep 2doneecho"❌ 健康检查失败! ($TARGET)"return 1}check_health
脚本C:数据库自动备份
#!/bin/bash# 文件名: db_backup.sh# 用法: 添加到crontab: 0 2 * * * /path/to/db_backup.shDB_NAME="myapp"DB_USER="backup_user"DB_PASS="backup_pass"BACKUP_DIR="/backups/mysql"RETENTION_DAYS=30DATE=$(date +%Y%m%d_%H%M%S)BACKUP_FILE="$BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz"mkdir -p "$BACKUP_DIR"echo"开始备份数据库: $DB_NAME"# 执行备份(使用mysqldump)if mysqldump -u "$DB_USER" -p"$DB_PASS" \ --single-transaction \ --routines \ --triggers \"$DB_NAME" | gzip > "$BACKUP_FILE"; thenecho"✅ 备份成功: $BACKUP_FILE"echo" 大小: $(du -sh $BACKUP_FILE | cut -f1)"elseecho"❌ 备份失败!"exit 1fi# 清理旧备份echo"清理 $RETENTION_DAYS 天前的备份..."find "$BACKUP_DIR" -name "*.sql.gz" -mtime +$RETENTION_DAYS -deleteecho"✅ 备份任务完成"
第七步:Crontab定时任务
# 编辑crontabcrontab -e# 格式:分 时 日 月 周 命令# ============================================# 示例# ============================================0 2 * * * /scripts/db_backup.sh # 每天凌晨2点备份0 3 * * 0 /scripts/cleanup_logs.sh # 每周日凌晨3点清理日志*/5 * * * * /scripts/health_check.sh # 每5分钟健康检查0 8 * * 1-5 /scripts/daily_report.sh # 工作日早上8点生成报告# 查看已设置的定时任务crontab -l# 查看crontab日志grep CRON /var/log/syslog
实用技巧与避坑指南
- 1. 变量引用养成加引号的习惯:
"$file" 而不是 $file。如果文件名包含空格,不加引号会被拆分成多个参数。 - 2.
set -e 让脚本遇到错误就停止:在脚本开头加上 set -e,任何命令返回非0退出码就立即终止。加上 set -u 则在引用未定义变量时报错,避免变量名拼写错误导致的隐蔽bug。 - 3.
rm -rf 前先打印确认:在脚本中使用变量拼接的路径执行删除时,先用 echo 打印出目标路径,确认无误后再改为 rm。 - 4. 使用
shellcheck检查脚本:# 安装shellcheck(静态分析Shell脚本的工具)apt install shellcheckshellcheck script.sh
- 5. 记录脚本执行日志:
LOG_FILE="/var/log/script.log"exec 2>&1exec > >(tee -a "$LOG_FILE")# 后续所有输出都会同时显示在终端和写入日志文件
总结
本教程覆盖了从基础命令到生产级Shell脚本的完整学习路径:
掌握了这些,你就能把日常工作中的重复性操作封装成脚本,把时间省下来做更有价值的事。