1. Shell脚本入门:从零开始的自动化之旅
第一次接触Shell脚本时,我被它的简洁高效所震撼。记得有次需要批量重命名几百个日志文件,手动操作简直噩梦,而一个简单的for循环脚本3秒就搞定了。Shell脚本就像一把瑞士军刀,虽然不如专业编程语言功能强大,但在系统管理、文件操作和自动化任务方面,它能让你事半功倍。
Shell脚本本质上是命令行指令的集合,通过特定的语法组织起来,实现自动化执行。它特别适合处理文本、管理系统资源和串联多个命令。学习Shell不需要复杂的开发环境,任何Linux/Unix系统或Mac的终端都能直接编写运行,Windows用户通过WSL或Git Bash也能轻松上手。
2. Shell脚本核心语法精要
2.1 基础结构解析
每个Shell脚本都从shebang开始,这个特殊的注释告诉系统用哪个解释器执行脚本。最常见的是:
#!/bin/bash紧接着是注释(以#开头)和实际命令。一个完整的脚本示例:
#!/bin/bash # 这是注释:显示当前用户和目录 echo "用户: $USER" echo "目录: $(pwd)"变量定义和使用是基础中的基础:
name="张三" # 定义变量 echo ${name} # 使用变量注意:等号两边不能有空格,否则会被视为命令和参数
2.2 流程控制实战
条件判断是脚本智能化的关键:
if [ -f "/path/to/file" ]; then echo "文件存在" elif [ -d "/path/to/dir" ]; then echo "目录存在" else echo "都不存在" fi循环处理让批量操作成为可能:
# for循环示例 for i in {1..5}; do echo "第${i}次循环" done # while循环读取文件 while read line; do echo "行内容: $line" done < filename.txt2.3 函数与参数传递
封装重复操作为函数:
function greet() { local name=$1 # local定义局部变量 echo "Hello, ${name}!" } greet "李四" # 调用函数处理脚本参数:
echo "脚本名: $0" echo "第一个参数: $1" echo "参数总数: $#" echo "所有参数: $@"3. 实用脚本开发技巧
3.1 文件操作大全
检查文件属性:
[ -e file ] # 存在 [ -f file ] # 是普通文件 [ -d dir ] # 是目录 [ -s file ] # 大小>0批量重命名(我常用的日志处理脚本):
#!/bin/bash counter=1 for file in *.log; do mv "$file" "log_$(date +%Y%m%d)_${counter}.log" ((counter++)) done3.2 文本处理三剑客
grep查找内容:
grep "error" /var/log/syslog # 查找error关键词 grep -r "pattern" /some/dir # 递归搜索sed流编辑器:
sed 's/foo/bar/g' file.txt # 替换所有foo为bar sed -i.bak '1d' file.txt # 删除首行并备份原文件awk数据处理:
awk '{print $1}' access.log # 打印第一列 awk -F: '{print $1}' /etc/passwd # 以:为分隔符3.3 实用脚本片段
备份目录(带日期压缩):
#!/bin/bash backup_dir="/backups" target_dir="$HOME/documents" [ ! -d "$backup_dir" ] && mkdir -p "$backup_dir" tar -czf "${backup_dir}/docs_$(date +%Y%m%d).tar.gz" "$target_dir"监控磁盘空间报警:
#!/bin/bash threshold=90 usage=$(df / | awk 'NR==2 {print $5}' | tr -d '%') [ "$usage" -ge "$threshold" ] && mail -s "磁盘告警" admin@example.com <<< "根分区使用率已达${usage}%"4. 调试与优化实战
4.1 调试技巧
使用set命令开启调试模式:
#!/bin/bash set -x # 开启命令打印 # 你的代码 set +x # 关闭调试更专业的调试方法:
#!/bin/bash -v # 打印读取的命令 #!/bin/bash -x # 打印执行的命令4.2 错误处理
防止脚本因错误中断:
set -e # 任何命令返回非零状态时立即退出 set -o pipefail # 管道命令中任意失败都视为失败 # 或者针对特定命令 command || { echo "命令执行失败" exit 1 }记录日志的最佳实践:
log() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] $@" >> script.log } log "开始执行任务" important_command || log "命令执行失败" log "任务完成"4.3 性能优化
减少子进程创建:
# 差:每次循环都启动新进程 for i in {1..100}; do count=$(wc -l file.txt) done # 好:一次性读取 line_count=$(wc -l < file.txt) for i in {1..100}; do # 使用$line_count done选择更快的命令替代:
# 慢 cat file | grep "pattern" # 快 grep "pattern" file5. 实际案例:服务器监控脚本开发
5.1 需求分析
开发一个完整的服务器监控脚本,需要:
- 检查CPU/内存/磁盘使用率
- 监控关键服务状态
- 生成易读的报告
- 支持邮件报警
5.2 完整实现
#!/bin/bash # 服务器健康监控脚本 # 配置 THRESHOLD_CPU=80 THRESHOLD_MEM=85 THRESHOLD_DISK=90 EMAIL="admin@example.com" LOG_FILE="/var/log/health_monitor.log" # 初始化日志 log() { echo "[$(date +'%F %T')] $@" | tee -a $LOG_FILE } # 检查CPU使用率 check_cpu() { local usage=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1}') if (( $(echo "$usage > $THRESHOLD_CPU" | bc -l) )); then log "CPU使用率过高: ${usage}%" return 1 fi return 0 } # 检查内存使用 check_mem() { local total=$(free -m | awk '/Mem:/ {print $2}') local used=$(free -m | awk '/Mem:/ {print $3}') local usage=$((used*100/total)) if [ $usage -gt $THRESHOLD_MEM ]; then log "内存使用率过高: ${usage}%" return 1 fi return 0 } # 检查磁盘空间 check_disk() { local result=0 while read line; do local partition=$(echo $line | awk '{print $1}') local usage=$(echo $line | awk '{print $5}' | tr -d '%') if [ $usage -gt $THRESHOLD_DISK ]; then log "磁盘空间告警: ${partition} ${usage}%" result=1 fi done < <(df -h | awk 'NR>1 {print $1,$5}') return $result } # 主函数 main() { log "开始服务器健康检查" local alert=false check_cpu || alert=true check_mem || alert=true check_disk || alert=true if $alert; then mail -s "服务器健康告警" $EMAIL < $LOG_FILE log "已发送告警邮件" else log "所有检查项正常" fi } main "$@"5.3 部署建议
- 保存为
/usr/local/bin/health_monitor.sh - 添加执行权限:
chmod +x /usr/local/bin/health_monitor.sh - 设置cron定时任务:
# 每30分钟运行一次 */30 * * * * /usr/local/bin/health_monitor.sh - 日志轮转配置(/etc/logrotate.d/health_monitor):
/var/log/health_monitor.log { daily rotate 7 compress missingok notifempty }
6. 进阶学习路径
6.1 推荐学习资源
书籍:
- 《Linux命令行与Shell脚本编程大全》
- 《Shell脚本学习指南》
在线文档:
- Bash官方文档:
man bash - GNU Bash手册:https://www.gnu.org/software/bash/manual/
- Bash官方文档:
实战平台:
- OverTheWire Bandit游戏:渐进式学习Shell命令
- Codewars Bash练习:挑战脚本编写
6.2 常见陷阱与解决方案
引号使用问题:
# 错误:文件名含空格时会出错 for file in $(ls); do echo $file done # 正确:处理含空格文件名 for file in *; do echo "$file" done变量未定义检查:
# 不安全 rm -rf $DIRECTORY/* # 安全做法 : ${DIRECTORY:?"未设置目录变量"} rm -rf "${DIRECTORY}/"*6.3 性能对比测试
测试不同循环方式的性能差异:
# 测试10000次循环 echo "测试for循环:" time for ((i=0; i<10000; i++)); do : # 空操作 done echo "测试while循环:" time i=0; while [ $i -lt 10000 ]; do ((i++)) done echo "测试seq+for:" time for i in $(seq 1 10000); do : done测试结果通常显示C风格的for循环最快,而依赖外部命令的seq最慢。这个简单的测试告诉我们:在脚本中尽量减少外部命令调用,使用内置语法能显著提高性能。