当前位置: 首页 > news >正文

Shell 脚本编程全解析:从入门到企业级实战

Shell 脚本编程全解析:从入门到企业级实战

一、Shell 脚本基础认知

1.1 什么是 Shell

Shell 是一种命令行解释器,它为用户提供了与操作系统内核交互的接口。简单来说,Shell 就是用户与 Linux 系统之间的 “翻译官”,将用户的命令转换为内核能够理解的指令。

而 Shell 脚本(Shell Script)则是将一系列 Shell 命令按顺序保存到文本文件中,赋予执行权限后可以批量执行的脚本文件。它类似于 Windows 系统的批处理文件(.bat),但功能更加强大、语法更加灵活。

1.2 Shell 的种类

Linux 系统中常见的 Shell 有多种类型:

  • Bash(Bourne Again SHell):最常用的 Shell,是 Bourne Shell 的增强版,兼容 Bourne Shell 并增加了许多新特性
  • Sh(Bourne Shell):最早的 Unix Shell,功能相对简单
  • Csh(C Shell):语法类似 C 语言,有自己的特点
  • Ksh(Korn Shell):结合了 Csh 和 Sh 的优点
  • Zsh:功能更强大的 Shell,兼容 Bash 并增加了更多特性

在主流 Linux 发行版(如 CentOS、Ubuntu)中,默认的 Shell 都是 Bash,因此本文主要围绕 Bash 脚本展开。

可以通过以下命令查看系统默认 Shell:

echo $SHELL

查看系统安装的所有 Shell:

cat /etc/shells

二、为什么要学习 Shell 脚本编程

2.1 自动化运维的核心工具

在 Linux 服务器管理中,许多重复性工作可以通过 Shell 脚本自动化完成:

  • 系统监控与报警
  • 日志分析与统计
  • 批量部署与配置
  • 定时任务执行
  • 数据备份与恢复

2.2 处理文本文件的优势

Linux 系统中绝大多数配置文件、日志文件都是纯文本格式,Shell 脚本结合文本处理工具(grep、sed、awk 等)可以高效处理这些文件:

  • 快速提取关键信息
  • 批量修改配置参数
  • 分析日志并生成报告
  • 处理格式化数据

2.3 职业发展的必备技能

对于运维工程师、开发工程师、测试工程师而言,Shell 脚本编程是一项重要技能:

  • 提升工作效率,减少重复劳动
  • 实现复杂的系统管理任务
  • 更好地理解 Linux 系统原理
  • 为学习更复杂的编程语言打下基础

三、Shell 脚本入门准备

3.1 环境准备

学习 Shell 脚本编程不需要特殊的开发环境,只需要:

  1. 一台 Linux 系统的计算机或服务器
  2. 一个文本编辑器(推荐 vim 或 nano)
  3. 基本的 Linux 命令操作能力

3.2 vim 编辑器配置

vim 是编写 Shell 脚本的理想工具,以下是一些实用配置:

# 创建或编辑 vim 配置文件
vim ~/.vimrc# 添加以下配置
set number          # 显示行号
set syntax on       # 语法高亮
set autoindent      # 自动缩进
set smartindent     # 智能缩进
set tabstop=4       # Tab 键宽度
set shiftwidth=4    # 自动缩进宽度
set showmatch       # 括号匹配显示

配置生效后,使用 vim 编辑脚本会更加高效。

四、第一个 Shell 脚本

4.1 脚本创建与执行

创建一个简单的 Shell 脚本:

# 创建脚本文件
vim hello.sh# 输入以下内容
#!/bin/bash
# 这是一个简单的 Shell 脚本
echo "Hello, World!"
echo "当前时间: $(date)"
echo "当前用户: $USER"
echo "当前目录: $(pwd)"

脚本执行方法:

  1. 使用 bash 命令执行(不需要执行权限):bash hello.sh

  2. 赋予执行权限后直接执行

# 添加执行权限
chmod +x hello.sh# 执行脚本
./hello.sh
  1. 使用绝对路径执行
# 假设脚本在 /home/user 目录下
/home/user/hello.sh
  1. 使用 source 命令执行(在当前 Shell 环境中执行):
source hello.sh
# 或
. hello.sh

4.2 脚本结构解析

一个规范的 Shell 脚本通常包含以下几个部分:

  1. 解释器声明:第一行的 #!/bin/bash 指定脚本使用 bash 解释器
  2. 注释信息:以 # 开头的行,用于说明脚本功能、作者、版本等
  3. 脚本主体:具体的命令和控制结构
#!/bin/bash
# 脚本名称: system_info.sh
# 作    者: Your Name
# 日    期: 2023-07-01
# 版    本: 1.0
# 功    能: 显示系统基本信息echo "=== 系统信息 ==="
uname -aecho -e "\n=== CPU 信息 ==="
lscpu | grep 'Model name\|CPU(s)'echo -e "\n=== 内存信息 ==="
free -hecho -e "\n=== 磁盘信息 ==="
df -h

五、Shell 变量与数据类型

5.1 变量定义与使用

Shell 变量不需要声明类型,直接定义即可:

#!/bin/bash# 定义变量(等号前后不能有空格)
name="John"
age=30
height=1.75# 使用变量(在变量名前加 $)
echo "姓名: $name"
echo "年龄: $age"
echo "身高: $height"# 变量重新赋值
age=31
echo "修改后的年龄: $age"# 变量拼接
greeting="Hello, "$name"!"
echo $greeting

5.2 系统环境变量

系统预设了一些环境变量,可以直接使用:

#!/bin/bashecho "当前用户: $USER"
echo "用户家目录: $HOME"
echo "当前工作目录: $PWD"
echo "命令搜索路径: $PATH"
echo "主机名: $HOSTNAME"
echo "Shell 类型: $SHELL"
echo "终端类型: $TERM"
echo "进程 ID: $$"  # 当前脚本的进程 ID
echo "退出状态: $?"  # 上一条命令的退出状态,0 表示成功

5.3 位置参数变量

用于获取脚本执行时传入的参数:

#!/bin/bashecho "脚本名称: $0"
echo "第一个参数: $1"
echo "第二个参数: $2"
echo "第三个参数: $3"
echo "所有参数: $*"  # 所有参数作为一个整体
echo "所有参数: $@"  # 所有参数作为独立个体
echo "参数个数: $#"  # 参数的数量

执行示例:

./params.sh first second third

5.4 变量操作

#!/bin/bash# 字符串长度
str="Hello World"
echo "字符串长度: ${#str}"# 字符串截取
echo "从位置 1 开始截取: ${str:1}"
echo "从位置 1 开始截取 5 个字符: ${str:1:5}"# 字符串替换
echo "替换第一个匹配: ${str/Hello/Hi}"
echo "替换所有匹配: ${str//o/O}"# 变量默认值
echo "变量存在: ${name:-默认值}"  # 如果 name 未定义,使用默认值
name="John"
echo "变量存在: ${name:-默认值}"  # 如果 name 已定义,使用变量值

六、Shell 运算符与表达式

6.1 算术运算符

#!/bin/basha=10
b=3# 算术运算方式 1: 使用 $((...))
echo "a + b = $((a + b))"
echo "a - b = $((a - b))"
echo "a * b = $((a * b))"
echo "a / b = $((a / b))"
echo "a % b = $((a % b))"
echo "a^2 = $((a **2))"# 算术运算方式 2: 使用 $[...])
echo "a + b = $[a + b]"# 算术运算方式 3: 使用 expr 命令
echo "a + b = $(expr $a + $b)"
echo "a * b = $(expr $a \* $b)"  # 注意乘法需要转义

6.2 赋值运算符

#!/bin/basha=10# 基本赋值
b=$a
echo "b = $b"# 复合赋值
b=5
b+=3  # 等价于 b=$((b + 3))
echo "b += 3 后: $b"b=5
b*=2  # 等价于 b=$((b * 2))
echo "b *= 2 后: $b"

6.3 比较运算符

6.3.1 数值比较
#!/bin/basha=10
b=20# 使用 [ ] 进行比较,注意空格
if [ $a -eq $b ]; thenecho "$a 等于 $b"
elseecho "$a 不等于 $b"
fiif [ $a -gt $b ]; thenecho "$a 大于 $b"
elseecho "$a 不大于 $b"
fi# 常用数值比较运算符
# -eq: 等于
# -ne: 不等于
# -gt: 大于
# -lt: 小于
# -ge: 大于等于
# -le: 小于等于
6.3.2 字符串比较
#!/bin/bashstr1="hello"
str2="world"
str3="hello"if [ "$str1" = "$str2" ]; thenecho "$str1 等于 $str2"
elseecho "$str1 不等于 $str2"
fiif [ "$str1" = "$str3" ]; thenecho "$str1 等于 $str3"
elseecho "$str1 不等于 $str3"
fi# 字符串长度比较
if [ -z "$str1" ]; thenecho "$str1 是空字符串"
elseecho "$str1 不是空字符串,长度为 ${#str1}"
fi# 常用字符串比较运算符
# =: 等于
# !=: 不等于
# -z: 字符串长度为 0
# -n: 字符串长度不为 0

6.4 逻辑运算符

#!/bin/basha=10
b=20
c=30# 逻辑与 -a 或 &&
if [ $a -lt $b -a $b -lt $c ]; thenecho "$a < $b < $c 成立"
fiif [ $a -lt $b ] && [ $b -lt $c ]; thenecho "$a < $b < $c 成立"
fi# 逻辑或 -o 或 ||
if [ $a -gt $b -o $b -lt $c ]; thenecho "条件成立"
fiif [ $a -gt $b ] || [ $b -lt $c ]; thenecho "条件成立"
fi# 逻辑非 !
if [ ! $a -gt $b ]; thenecho "$a 不大于 $b"
fi

6.5 文件测试运算符

#!/bin/bashfile="test.txt"
dir="/tmp"# 检查文件是否存在
if [ -e "$file" ]; thenecho "$file 存在"
elseecho "$file 不存在"touch "$file"echo "已创建 $file"
fi# 检查是否为普通文件
if [ -f "$file" ]; thenecho "$file 是普通文件"
fi# 检查是否为目录
if [ -d "$dir" ]; thenecho "$dir 是目录"
fi# 检查文件是否可执行
if [ -x "$file" ]; thenecho "$file 可执行"
elseecho "$file 不可执行"chmod +x "$file"echo "已设置 $file 可执行权限"
fi# 常用文件测试运算符
# -e: 文件或目录存在
# -f: 是普通文件
# -d: 是目录
# -r: 可读
# -w: 可写
# -x: 可执行
# -s: 文件大小不为 0
# -L: 是符号链接

七、流程控制语句

7.1 if 条件语句

7.1.1 基本语法
#!/bin/bash# 单分支 if 语句
age=18
if [ $age -ge 18 ]; thenecho "已成年"
fi# 双分支 if 语句
score=75
if [ $score -ge 60 ]; thenecho "成绩合格"
elseecho "成绩不合格"
fi# 多分支 if 语句
if [ $score -ge 90 ]; thenecho "优秀"
elif [ $score -ge 80 ]; thenecho "良好"
elif [ $score -ge 60 ]; thenecho "及格"
elseecho "不及格"
fi
7.1.2 实际应用示例
#!/bin/bash
# 检查服务是否运行service_name="sshd"# 检查服务状态
if systemctl is-active --quiet $service_name; thenecho "$service_name 服务正在运行"# 检查服务是否设置为开机启动if systemctl is-enabled --quiet $service_name; thenecho "$service_name 服务已设置为开机启动"elseecho "$service_name 服务未设置为开机启动"read -p "是否设置 $service_name 为开机启动? (y/n) " choiceif [ "$choice" = "y" ] || [ "$choice" = "Y" ]; thensystemctl enable $service_nameecho "$service_name 已设置为开机启动"fifi
elseecho "$service_name 服务未运行"read -p "是否启动 $service_name 服务? (y/n) " choiceif [ "$choice" = "y" ] || [ "$choice" = "Y" ]; thensystemctl start $service_nameif [ $? -eq 0 ]; thenecho "$service_name 服务启动成功"elseecho "$service_name 服务启动失败"fifi
fi

7.2 case 语句

当需要匹配多个条件时,case 语句比 if 语句更简洁:

#!/bin/bash
# 服务管理脚本if [ $# -ne 1 ]; thenecho "用法: $0 [start|stop|restart|status]"exit 1
fiservice_name="httpd"case "$1" instart)systemctl start $service_nameecho "$service_name 服务已启动";;stop)systemctl stop $service_nameecho "$service_name 服务已停止";;restart)systemctl restart $service_nameecho "$service_name 服务已重启";;status)systemctl status $service_name;;*)echo "无效参数: $1"echo "用法: $0 [start|stop|restart|status]"exit 1;;
esac

7.3 for 循环

7.3.1 基本语法
#!/bin/bash# 遍历列表
fruits="apple banana cherry date"
for fruit in $fruits; doecho "水果: $fruit"
done# 遍历数字范围
echo -e "\n计数从 1 到 5:"
for i in {1..5}; doecho $i
done# 遍历带步长的数字范围
echo -e "\n偶数从 2 到 10:"
for i in {2..10..2}; doecho $i
done# C 风格的 for 循环
echo -e "\nC 风格循环:"
for ((i=1; i<=5; i++)); doecho $i
done
7.3.2 实际应用示例
#!/bin/bash
# 批量检查主机连通性# 主机列表
hosts="192.168.1.1 192.168.1.10 192.168.1.20 www.baidu.com www.google.com"# 检查每个主机
for host in $hosts; doecho -n "检查 $host: "# 发送 2 个 ICMP 包,超时时间 2 秒ping -c 2 -W 2 $host &>/dev/nullif [ $? -eq 0 ]; thenecho "连通"elseecho "不通"fi
done

7.4 while 循环

while 循环在条件为真时重复执行命令:

#!/bin/bash# 基本 while 循环
count=1
echo "计数到 5:"
while [ $count -le 5 ]; doecho $countcount=$((count + 1))# 或者使用 let 命令: let count++
done# 读取文件内容
echo -e "\n读取文件内容:"
file="test.txt"
# 创建测试文件
echo "第一行" > $file
echo "第二行" >> $file
echo "第三行" >> $file# 使用 while 循环读取文件
while read line; doecho "行内容: $line"
done < $file# 删除测试文件
rm -f $file# 无限循环(按 Ctrl+C 退出)
echo -e "\n无限循环,按 Ctrl+C 退出"
count=1
while true; doecho "循环次数: $count"count=$((count + 1))sleep 1# 循环 5 次后退出if [ $count -gt 5 ]; thenbreakfi
done

7.5 until 循环

until 循环与 while 循环相反,在条件为假时重复执行命令:

#!/bin/bash# 基本 until 循环
count=1
echo "计数到 5:"
until [ $count -gt 5 ]; doecho $countlet count++
done# 实际应用:等待服务启动
echo -e "\n等待 httpd 服务启动..."
until systemctl is-active --quiet httpd; doecho "httpd 服务尚未启动,等待 3 秒..."sleep 3
done
echo "httpd 服务已启动"

7.6 循环控制语句

break 和 continue 用于控制循环流程:

#!/bin/bash# break 示例:找到第一个偶数就退出
echo "寻找 1-10 中的第一个偶数:"
for num in {1..10}; doif [ $((num % 2)) -eq 0 ]; thenecho "找到偶数: $num"break  # 退出循环fiecho "检查: $num"
done# continue 示例:只显示奇数
echo -e "\n显示 1-10 中的奇数:"
for num in {1..10}; doif [ $((num % 2)) -eq 0 ]; thencontinue  # 跳过本次循环的剩余部分fiecho $num
done# break N 和 continue N:控制多层循环
echo -e "\n多层循环控制:"
for i in {1..3}; doecho "外层循环: $i"for j in {1..3}; doif [ $j -eq 2 ]; thenbreak 2  # 退出两层循环# continue 2  # 跳过外层循环的当前迭代fiecho "  内层循环: $j"done
done

八、函数与模块化

8.1 函数定义与调用

#!/bin/bash# 基本函数定义
hello() {echo "Hello, World!"
}# 调用函数
echo "调用 hello 函数:"
hello# 带参数的函数
greet() {echo "Hello, $1!"echo "函数参数个数: $#"echo "所有参数: $*"
}echo -e "\n调用 greet 函数:"
greet "John" "Doe"# 带返回值的函数
add() {local sum=$(( $1 + $2 ))  # local 声明局部变量echo $sum  # 通过 echo 返回值
}echo -e "\n调用 add 函数:"
result=$(add 10 20)
echo "10 + 20 = $result"

8.2 函数库与模块化

将常用函数放在单独的文件中,需要时引入使用:

  1. 创建函数库文件 myfuncs.sh
#!/bin/bash# 显示错误信息
error() {echo "ERROR: $1" >&2
}# 显示信息并退出
die() {error "$1"exit 1
}# 检查命令是否存在
command_exists() {command -v "$1" >/dev/null 2>&1
}# 计算文件大小总和
dir_size() {if [ $# -ne 1 ] || [ ! -d "$1" ]; thenerror "请提供一个目录作为参数"return 1fidu -sh "$1" | awk '{print $1}'
}
  1. 在脚本中引入函数库:
#!/bin/bash# 引入函数库
source ./myfuncs.sh || die "无法加载函数库"# 使用函数库中的函数
echo "检查 curl 命令是否存在:"
if command_exists curl; thenecho "curl 已安装"
elseerror "curl 未安装"
fiecho -e "\n计算当前目录大小:"
dir_size .echo -e "\n测试错误处理:"
error "这是一个错误信息"
# die "这是一个致命错误,程序将退出"

九、文本处理三剑客

9.1 grep:文本搜索工具

#!/bin/bash# 创建测试文件
test_file="grep_test.txt"
cat > $test_file << EOF
apple
Banana
cherry
date
elderberry
fig
Grape
kiwi
lemon
mango
EOFecho "查找包含 'e' 的行:"
grep "e" $test_fileecho -e "\n忽略大小写查找包含 'a' 的行:"
grep -i "a" $test_fileecho -e "\n查找不包含 'e' 的行:"
grep -v "e" $test_fileecho -e "\n显示行号:"
grep -n "e" $test_fileecho -e "\n使用正则表达式查找以 'b' 或 'B' 开头的行:"
grep -i "^b" $test_file# 清理测试文件
rm -f $test_file

9.2 sed:流编辑器

#!/bin/bash# 创建测试文件
test_file="sed_test.txt"
cat > $test_file << EOF
apple
Banana
cherry
date
elderberry
fig
Grape
kiwi
lemon
mango
EOFecho "原始文件内容:"
cat $test_fileecho -e "\n将 'e' 替换为 'E':"
sed 's/e/E/' $test_fileecho -e "\n全局替换 'e' 为 'E':"
sed 's/e/E/g' $test_fileecho -e "\n删除包含 'a' 的行:"
sed '/a/d' $test_fileecho -e "\n在行首添加序号:"
sed '=' $test_file | sed 'N;s/\n/. /'echo -e "\n在文件中插入一行:"
sed '3i inserted line' $test_file# 原地修改文件(备份原始文件)
sed -i.bak 's/^/fruit: /' $test_file
echo -e "\n原地修改后的文件:"
cat $test_file# 清理测试文件
rm -f $test_file $test_file.bak

9.3 awk:文本处理语言

#!/bin/bash# 创建测试文件
test_file="awk_test.txt"
cat > $test_file << EOF
Name    Age     City
Alice   25      New York
Bob     30      London
Charlie 35      Paris
David   40      Tokyo
EOFecho "原始文件内容:"
cat $test_fileecho -e "\n打印第一列和第三列:"
awk '{print $1, $3}' $test_fileecho -e "\n跳过表头,打印年龄大于 30 的行:"
awk 'NR > 1 && $2 > 30 {print $1, "is", $2, "years old"}' $test_fileecho -e "\n计算平均年龄:"
awk 'NR > 1 {sum += $2; count++} END {print "平均年龄:", sum/count}' $test_fileecho -e "\n格式化输出:"
awk 'NR == 1 {printf "%-10s %-5s %-10s\n", $1, $2, $3} NR > 1 {printf "%-10s %-5d %-10s\n", $1, $2, $3}' $test_file# 清理测试文件
rm -f $test_file

十、企业级实战案例

10.1 系统监控脚本

#!/bin/bash
# 系统监控脚本,定期检查系统资源使用情况# 配置
THRESHOLD_CPU=80    # CPU 使用率阈值(%)
THRESHOLD_MEM=80    # 内存使用率阈值(%)
THRESHOLD_DISK=80   # 磁盘使用率阈值(%)
LOG_FILE="/var/log/system_monitor.log"
CHECK_INTERVAL=60   # 检查间隔(秒)# 日志记录函数
log() {local timestamp=$(date "+%Y-%m-%d %H:%M:%S")echo "[$timestamp] $1" >> $LOG_FILE
}# 检查 CPU 使用率
check_cpu() {local cpu_usage=$(top -b -n 1 | grep "%Cpu(s)" | awk '{print $2 + $4}')local cpu_usage_int=$(printf "%.0f" $cpu_usage)if [ $cpu_usage_int -ge $THRESHOLD_CPU ]; thenlocal message="CPU 使用率过高: $cpu_usage_int%"echo $messagelog $message# 可以在这里添加报警逻辑,如发送邮件elseecho "CPU 使用率正常: $cpu_usage_int%"fi
}# 检查内存使用率
check_memory() {local mem_usage=$(free | grep Mem | awk '{print $3/$2 * 100.0}')local mem_usage_int=$(printf "%.0f" $mem_usage)if [ $mem_usage_int -ge $THRESHOLD_MEM ]; thenlocal message="内存使用率过高: $mem_usage_int%"echo $messagelog $message# 可以在这里添加报警逻辑,如发送邮件elseecho "内存使用率正常: $mem_usage_int%"fi
}# 检查磁盘使用率
check_disk() {local disk_usage=$(df -h / | tail -1 | awk '{print $5}' | sed 's/%//')if [ $disk_usage -ge $THRESHOLD_DISK ]; thenlocal message="根目录磁盘使用率过高: $disk_usage%"echo $messagelog $message# 可以在这里添加报警逻辑,如发送邮件elseecho "根目录磁盘使用率正常: $disk_usage%"fi
}# 检查系统负载
check_load() {local load_avg=$(uptime | awk -F 'load average: ' '{print $2}' | cut -d',' -f1)local cpu_cores=$(grep -c ^processor /proc/cpuinfo)echo "系统负载平均值(1分钟): $load_avg (CPU核心数: $cpu_cores)"
}# 主函数
main() {echo "=== 系统监控报告 $(date "+%Y-%m-%d %H:%M:%S") ==="check_cpucheck_memorycheck_diskcheck_loadecho "==========================================="echo
}# 如果脚本带有参数 "once",则只运行一次
if [ "$1" = "once" ]; thenmain
else# 否则循环运行while true; domainsleep $CHECK_INTERVALdone
fi

10.2 日志分析脚本

#!/bin/bash
# Nginx 访问日志分析脚本# 配置
LOG_FILE="/var/log/nginx/access.log"
TOP_N=10  # 显示前 N 条记录# 检查日志文件是否存在
if [ ! -f "$LOG_FILE" ]; thenecho "错误: 日志文件 $LOG_FILE 不存在"exit 1
fi# 显示菜单
show_menu() {echo "Nginx 日志分析工具"echo "1. 访问量最高的 IP 地址"echo "2. 访问量最高的 URL"echo "3. 访问量最高的 HTTP 状态码"echo "4. 访问量最高的用户代理(UA)"echo "5. 按小时统计访问量"echo "6. 退出"echo -n "请选择(1-6): "
}# 分析访问量最高的 IP
analyze_top_ips() {echo -e "\n访问量最高的 $TOP_N 个 IP 地址:"awk '{print $1}' $LOG_FILE | sort | uniq -c | sort -nr | head -n $TOP_N
}# 分析访问量最高的 URL
analyze_top_urls() {echo -e "\n访问量最高的 $TOP_N 个 URL:"awk '{print $7}' $LOG_FILE | sort | uniq -c | sort -nr | head -n $TOP_N
}# 分析访问量最高的状态码
analyze_top_status() {echo -e "\n访问量最高的 $TOP_N 个 HTTP 状态码:"awk '{print $9}' $LOG_FILE | sort | uniq -c | sort -nr | head -n $TOP_N
}# 分析访问量最高的用户代理
analyze_top_ua() {echo -e "\n访问量最高的 $TOP_N 个用户代理:"awk -F'"' '{print $6}' $LOG_FILE | sort | uniq -c | sort -nr | head -n $TOP_N
}# 按小时统计访问量
analyze_hourly() {echo -e "\n按小时统计访问量:"awk '{print substr($4, 14, 5)}' $LOG_FILE | sort | uniq -c | sort -n
}# 主循环
while true; doshow_menuread choicecase $choice in1)analyze_top_ips;;2)analyze_top_urls;;3)analyze_top_status;;4)analyze_top_ua;;5)analyze_hourly;;6)echo "退出程序"exit 0;;*)echo "无效选择,请重新输入";;esacecho -e "\n按 Enter 键继续..."readclear
done

10.3 自动备份脚本

#!/bin/bash
# 自动备份脚本,支持文件和数据库备份# 配置
BACKUP_DIR="/backup"               # 备份存储目录
SOURCE_DIRS="/etc /var/www"        # 需要备份的目录,用空格分隔
DB_NAME="mydatabase"               # 数据库名称
DB_USER="backupuser"               # 数据库用户名
DB_PASS="backuppassword"           # 数据库密码
RETENTION_DAYS=7                   # 备份保留天数
DATE=$(date +%Y%m%d_%H%M%S)        # 当前日期时间
BACKUP_FILE="$BACKUP_DIR/full_backup_$DATE.tar.gz"  # 备份文件名# 确保备份目录存在
mkdir -p $BACKUP_DIR# 日志函数
log() {echo "[$(date +%Y-%m-%d %H:%M:%S)] $1"
}# 检查依赖命令
check_dependencies() {local dependencies=("tar" "mysqldump" "gzip")for dep in "${dependencies[@]}"; doif ! command -v $dep >/dev/null 2>&1; thenlog "错误: 未找到命令 $dep,请安装后再运行"exit 1fidone
}# 备份文件
backup_files() {log "开始备份文件..."local temp_dir=$(mktemp -d)# 复制要备份的目录for dir in $SOURCE_DIRS; doif [ -d "$dir" ]; thenmkdir -p $temp_dir$(dirname $dir)cp -a $dir $temp_dir$dirlog "已备份目录: $dir"elselog "警告: 目录 $dir 不存在,跳过备份"fidone# 备份数据库log "开始备份数据库 $DB_NAME..."mysqldump -u$DB_USER -p$DB_PASS $DB_NAME > $temp_dir/${DB_NAME}_backup.sqlif [ $? -eq 0 ]; thenlog "数据库备份成功"elselog "警告: 数据库备份失败"fi# 打包备份文件log "正在打包备份文件..."tar -zcf $BACKUP_FILE -C $temp_dir .# 清理临时目录rm -rf $temp_dirif [ -f $BACKUP_FILE ]; thenlog "备份文件已创建: $BACKUP_FILE"log "备份大小: $(du -h $BACKUP_FILE | awk '{print $1}')"return 0elselog "错误: 备份文件创建失败"return 1fi
}# 清理旧备份
cleanup_old_backups() {log "开始清理 $RETENTION_DAYS 天前的旧备份..."find $BACKUP_DIR -name "full_backup_*.tar.gz" -type f -mtime +$RETENTION_DAYS -deletelog "旧备份清理完成"
}# 主函数
main() {log "===== 开始执行自动备份 ====="check_dependenciesif backup_files; thencleanup_old_backupslog "===== 自动备份执行完成 ====="exit 0elselog "===== 自动备份执行失败 ====="exit 1fi
}# 执行主函数
main

十一、Shell 脚本调试与优化

11.1 脚本调试方法

#!/bin/bash# 调试方法 1: 使用 -x 选项执行脚本,显示执行的每一行命令
# bash -x script.sh# 调试方法 2: 在脚本中使用 set -x 和 set +x 控制调试范围
# set -x  # 开始调试
# 要调试的代码
# set +x  # 结束调试# 调试方法 3: 使用 -n 选项检查脚本语法错误
# bash -n script.sh# 示例脚本
name="John"
age=30echo "姓名: $name"
echo "年龄: $age"if [ $age -ge 18 ]; thenecho "已成年"
elseecho "未成年"
fi

11.2 脚本优化技巧

  1. 减少子进程创建 :尽量使用 Shell 内置命令,减少外部命令调用

​ 2.优化循环 :避免在循环中执行耗时操作

  1. 使用管道替代临时文件

  2. 适当使用变量缓存结果 :避免重复计算

  3. 合并命令 **:减少进程创建开销

#!/bin/bash# 优化前
start_time=$(date +%s)
for i in {1..1000}; do# 每次循环都创建新进程echo $i >> numbers.txt
done
end_time=$(date +%s)
echo "优化前耗时: $((end_time - start_time)) 秒"
rm numbers.txt# 优化后
start_time=$(date +%s)
# 只创建一个进程
{for i in {1..1000}; doecho $idone
} >> numbers.txt
end_time=$(date +%s)
echo "优化后耗时: $((end_time - start_time)) 秒"
rm numbers.txt

十二、Shell 脚本最佳实践

1.** 脚本开头指定解释器 :#!/bin/bash

2.添加脚本描述信息 :包括功能、作者、版本等

3.检查脚本所需依赖 :确保必要的命令和工具存在

4.处理错误情况 :使用 set -e 自动退出错误,或显式检查错误

5.使用函数组织代码 :提高可读性和复用性

6.变量引用加引号 :防止空格等特殊字符引起的问题

7.使用局部变量 :函数内部变量使用 local 声明

8.避免使用硬编码 :配置参数集中定义,便于修改

9.添加注释 :解释复杂逻辑和关键步骤

  1. 进行边界测试 **:确保脚本在异常情况下的稳定性

十三、总结与进阶学习

Shell 脚本编程是 Linux 系统管理和自动化运维的重要技能,要进一步提升 Shell 脚本编程能力,可以:

  1. 阅读系统自带的 Shell 脚本(如 /etc/init.d/ 目录下的服务脚本)
  2. 学习更复杂的文本处理和正则表达式
  3. 掌握 Shell 脚本的调试和性能优化技巧
  4. 了解不同 Shell(如 zsh)的特性和扩展功能
http://www.dtcms.com/a/466854.html

相关文章:

  • 嘉兴做网站优化杭州小周seo
  • 把网站做成app大冶市建设局网站
  • 吉林分销网站建设视频转文字网页
  • 厦门响应式网站网页做推广
  • 《中国个人信息保护法》解读与实践案例分析
  • 解除网站开发合同 首付款是否退花都区网站建设
  • SQL 注入详解:从原理到实战
  • 校园网站建设情况抽奖网站做的下去吗
  • 5.数据分析Matplotlib(数据可视化)
  • 西双版纳网站制作公司网站集约化建设题目
  • hot100的解析
  • 企业网站建设上机考试微信推广引流方法
  • 【LangChain】P19 LangChain Memory(一):让 AI 拥有“记忆力“的秘密
  • 揭阳网站制作案例宝安中心医院上班时间
  • 吴江开发区建设局网站如何建立网站站点
  • 娱乐公司网站模板西安市网站搭建
  • 做网站运用的软件郑州百姓网免费发布信息网
  • Ansible安装及模块
  • 揭阳网站建设公司全民建网站
  • 哪些网站可以做调查赚钱wordpress服务器镜像
  • AI + 区块链开发实战:3 大技术方向 + 5 个落地案例,解锁去中心化网络效能密码
  • 安阳网站建设方案自己做社交网站吗
  • Linux之curl常用参数介绍
  • 唐山seo网站建设提供网页制作平台的公司
  • 南阳市网站建设专利申请
  • 芜湖有哪些招聘网站阿里巴巴吧做网站
  • 网站做优化按点击收费做网站给韩国卖高仿
  • 做网站销售经常遇到的问题智能建站网站
  • Lambda表达式
  • SD12C.TCT瞬态电压抑制(TVS)二极管Semtech升特 电子元器件IC解析