第10课:Shell脚本编程

【腾讯云】语音识别准确率高,支持多语种,多场景,限时特惠,最低14.9元起

推广

【腾讯云】语音识别准确率高,支持多语种,多场景,限时特惠,最低14.9元起

Shell脚本编程

Shell脚本基础

1. 什么是Shell脚本

Shell脚本概念

# Shell脚本是包含Shell命令的文本文件
# 可以自动化执行系统管理任务
# 支持变量、条件判断、循环等编程结构

第一个Shell脚本

#!/bin/bash
# 这是一个简单的Shell脚本

echo "Hello, World!"
echo "当前时间: $(date)"
echo "当前用户: $(whoami)"
echo "当前目录: $(pwd)"

脚本执行方式

# 方式1:直接执行
chmod +x script.sh
./script.sh

# 方式2:使用bash执行
bash script.sh

# 方式3:使用source执行
source script.sh
# 或
. script.sh

2. Shebang行

常用Shebang

#!/bin/bash                   # 使用bash解释器
#!/bin/sh                     # 使用sh解释器
#!/usr/bin/env bash           # 使用env查找bash
#!/usr/bin/env python3        # Python脚本
#!/usr/bin/env node           # Node.js脚本

变量和参数

1. 变量定义和使用

变量定义

#!/bin/bash

# 定义变量 (注意等号两边不能有空格)
name="张三"
age=25
city="北京"

# 使用变量
echo "姓名: $name"
echo "年龄: $age"
echo "城市: ${city}"           # 推荐使用花括号

# 只读变量
readonly PI=3.14159
# PI=3.14                     # 这会报错

# 删除变量
unset age

变量类型

#!/bin/bash

# 字符串变量
str1="Hello World"
str2='Single quotes'
str3="包含变量: $str1"

# 数组变量
fruits=("苹果" "香蕉" "橙子")
echo "第一个水果: ${fruits[0]}"
echo "所有水果: ${fruits[@]}"
echo "数组长度: ${#fruits[@]}"

# 环境变量
export MY_VAR="这是环境变量"

2. 特殊变量

位置参数

#!/bin/bash
# 脚本名: params.sh

echo "脚本名: $0"
echo "第一个参数: $1"
echo "第二个参数: $2"
echo "第三个参数: $3"
echo "所有参数: $@"
echo "参数个数: $#"
echo "所有参数 (作为一个字符串): $*"

# 使用: ./params.sh arg1 arg2 arg3

特殊变量

#!/bin/bash

echo "当前进程ID: $$"
echo "上一个命令的退出状态: $?"
echo "后台运行的最后一个进程ID: $!"

# 命令执行状态
ls /nonexistent 2>/dev/null
if [ $? -eq 0 ]; then
    echo "命令执行成功"
else
    echo "命令执行失败"
fi

3. 变量操作

字符串操作

#!/bin/bash

str="Hello World Linux"

# 字符串长度
echo "字符串长度: ${#str}"

# 字符串截取
echo "从位置6开始: ${str:6}"
echo "从位置6开始,取5个字符: ${str:6:5}"

# 字符串替换
echo "替换第一个: ${str/o/O}"      # Hello WOrld Linux
echo "替换所有: ${str//o/O}"       # HellO WOrld Linux

# 字符串删除
echo "删除最短匹配: ${str#*o}"     # llo World Linux
echo "删除最长匹配: ${str##*o}"    # rld Linux
echo "从尾部删除最短: ${str%o*}"   # Hello World Lin
echo "从尾部删除最长: ${str%%o*}"  # Hell

变量默认值

#!/bin/bash

# 变量默认值设置
echo "USER变量: ${USER:-默认用户}"
echo "TEMP变量: ${TEMP:=临时目录}"

# 检查变量是否设置
echo "HOME变量: ${HOME:?HOME变量未设置}"

# 变量存在时使用替代值
echo "PATH存在时显示: ${PATH:+PATH已设置}"

条件判断

1. test命令和[]

文件测试

#!/bin/bash

file="/etc/passwd"

# 文件存在性测试
if [ -e "$file" ]; then
    echo "文件存在"
fi

if [ -f "$file" ]; then
    echo "是普通文件"
fi

if [ -d "/etc" ]; then
    echo "/etc是目录"
fi

if [ -r "$file" ]; then
    echo "文件可读"
fi

if [ -w "$file" ]; then
    echo "文件可写"
fi

if [ -x "/bin/ls" ]; then
    echo "/bin/ls可执行"
fi

字符串测试

#!/bin/bash

str1="hello"
str2="world"
str3=""

# 字符串比较
if [ "$str1" = "$str2" ]; then
    echo "字符串相等"
else
    echo "字符串不相等"
fi

# 字符串长度测试
if [ -z "$str3" ]; then
    echo "字符串为空"
fi

if [ -n "$str1" ]; then
    echo "字符串不为空"
fi

# 字符串包含 (需要使用[[ ]])
if [[ "$str1" == *"ell"* ]]; then
    echo "str1包含ell"
fi

数值比较

#!/bin/bash

num1=10
num2=20

if [ "$num1" -eq "$num2" ]; then
    echo "数值相等"
elif [ "$num1" -lt "$num2" ]; then
    echo "num1小于num2"
elif [ "$num1" -gt "$num2" ]; then
    echo "num1大于num2"
fi

# 数值比较操作符
# -eq  等于
# -ne  不等于
# -lt  小于
# -le  小于等于
# -gt  大于
# -ge  大于等于

2. if语句

if-else结构

#!/bin/bash

read -p "请输入一个数字: " number

if [ "$number" -gt 0 ]; then
    echo "正数"
elif [ "$number" -lt 0 ]; then
    echo "负数"
else
    echo "零"
fi

复合条件

#!/bin/bash

age=25
gender="male"

# 逻辑与 (&&)
if [ "$age" -ge 18 ] && [ "$gender" = "male" ]; then
    echo "成年男性"
fi

# 逻辑或 (||)
if [ "$age" -lt 18 ] || [ "$age" -gt 65 ]; then
    echo "非工作年龄"
fi

# 使用[[ ]]的复合条件
if [[ $age -ge 18 && $gender == "male" ]]; then
    echo "成年男性 (使用[[ ]])"
fi

3. case语句

case结构

#!/bin/bash

read -p "请选择操作 (start/stop/restart/status): " action

case $action in
    start)
        echo "启动服务..."
        ;;
    stop)
        echo "停止服务..."
        ;;
    restart)
        echo "重启服务..."
        ;;
    status)
        echo "查看状态..."
        ;;
    *)
        echo "无效选项"
        exit 1
        ;;
esac

模式匹配

#!/bin/bash

read -p "请输入文件名: " filename

case $filename in
    *.txt)
        echo "文本文件"
        ;;
    *.jpg|*.png|*.gif)
        echo "图片文件"
        ;;
    *.sh)
        echo "Shell脚本"
        ;;
    [Mm]akefile)
        echo "Makefile"
        ;;
    *)
        echo "未知文件类型"
        ;;
esac

循环结构

1. for循环

基本for循环

#!/bin/bash

# 遍历列表
for fruit in apple banana orange; do
    echo "水果: $fruit"
done

# 遍历数组
fruits=("苹果" "香蕉" "橙子")
for fruit in "${fruits[@]}"; do
    echo "水果: $fruit"
done

# 遍历文件
for file in *.txt; do
    if [ -f "$file" ]; then
        echo "处理文件: $file"
    fi
done

C风格for循环

#!/bin/bash

# C风格循环
for ((i=1; i<=10; i++)); do
    echo "数字: $i"
done

# 计算1到100的和
sum=0
for ((i=1; i<=100; i++)); do
    sum=$((sum + i))
done
echo "1到100的和: $sum"

遍历命令输出

#!/bin/bash

# 遍历用户列表
for user in $(cut -d: -f1 /etc/passwd); do
    echo "用户: $user"
done

# 遍历目录
for dir in $(ls -d */); do
    echo "目录: $dir"
done

2. while循环

while循环基础

#!/bin/bash

# 基本while循环
count=1
while [ $count -le 5 ]; do
    echo "计数: $count"
    count=$((count + 1))
done

# 读取文件内容
while read line; do
    echo "行内容: $line"
done < /etc/passwd

无限循环

#!/bin/bash

# 无限循环
while true; do
    echo "按Ctrl+C退出"
    sleep 1
done

# 或使用while :
while :; do
    echo "无限循环"
    sleep 1
done

3. until循环

until循环

#!/bin/bash

# until循环 (条件为假时执行)
count=1
until [ $count -gt 5 ]; do
    echo "计数: $count"
    count=$((count + 1))
done

4. 循环控制

break和continue

#!/bin/bash

# break示例
for i in {1..10}; do
    if [ $i -eq 6 ]; then
        break
    fi
    echo "数字: $i"
done

# continue示例
for i in {1..10}; do
    if [ $((i % 2)) -eq 0 ]; then
        continue
    fi
    echo "奇数: $i"
done

函数

1. 函数定义和调用

基本函数

#!/bin/bash

# 函数定义方式1
function greet() {
    echo "Hello, $1!"
}

# 函数定义方式2
say_goodbye() {
    echo "Goodbye, $1!"
}

# 函数调用
greet "World"
say_goodbye "Linux"

函数参数和返回值

#!/bin/bash

# 带参数的函数
add_numbers() {
    local num1=$1
    local num2=$2
    local result=$((num1 + num2))
    echo $result
}

# 带返回值的函数
is_even() {
    local number=$1
    if [ $((number % 2)) -eq 0 ]; then
        return 0  # 真
    else
        return 1  # 假
    fi
}

# 使用函数
result=$(add_numbers 10 20)
echo "10 + 20 = $result"

if is_even 4; then
    echo "4是偶数"
fi

2. 局部变量和全局变量

变量作用域

#!/bin/bash

global_var="全局变量"

test_scope() {
    local local_var="局部变量"
    global_var="修改后的全局变量"
    
    echo "函数内部:"
    echo "  局部变量: $local_var"
    echo "  全局变量: $global_var"
}

echo "函数调用前: $global_var"
test_scope
echo "函数调用后: $global_var"
# echo "局部变量: $local_var"  # 这会报错,局部变量不可访问

输入输出

1. 用户输入

read命令

#!/bin/bash

# 基本输入
read -p "请输入您的姓名: " name
echo "您好, $name!"

# 隐藏输入 (密码)
read -s -p "请输入密码: " password
echo
echo "密码已设置"

# 设置超时
if read -t 10 -p "请在10秒内输入 (超时自动继续): " input; then
    echo "您输入了: $input"
else
    echo "输入超时"
fi

# 限制输入字符数
read -n 1 -p "按任意键继续..." key
echo

2. 输出重定向

重定向操作

#!/bin/bash

# 输出重定向
echo "写入文件" > output.txt
echo "追加内容" >> output.txt

# 错误重定向
ls /nonexistent 2> error.log
ls /nonexistent 2>> error.log

# 同时重定向标准输出和错误输出
command > output.log 2>&1
command &> output.log

# 丢弃输出
command > /dev/null 2>&1

3. 管道和过滤

管道使用

#!/bin/bash

# 管道示例
ps aux | grep nginx | awk '{print $2}'

# 统计文件行数
cat /etc/passwd | wc -l

# 排序和去重
cat file.txt | sort | uniq

# 查找最大的文件
ls -la | sort -k5 -nr | head -5

实用脚本示例

1. 系统监控脚本

系统信息收集

#!/bin/bash

# 系统监控脚本
echo "=== 系统监控报告 ==="
echo "生成时间: $(date)"
echo

echo "=== 系统信息 ==="
echo "主机名: $(hostname)"
echo "内核版本: $(uname -r)"
echo "系统负载: $(uptime | awk -F'load average:' '{print $2}')"
echo

echo "=== 内存使用 ==="
free -h

echo
echo "=== 磁盘使用 ==="
df -h

echo
echo "=== CPU使用率最高的进程 ==="
ps aux --sort=-%cpu | head -6

echo
echo "=== 内存使用率最高的进程 ==="
ps aux --sort=-%mem | head -6

2. 备份脚本

自动备份脚本

#!/bin/bash

# 备份脚本
SOURCE_DIR="/home/user/documents"
BACKUP_DIR="/backup"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="backup_$DATE.tar.gz"

# 检查源目录
if [ ! -d "$SOURCE_DIR" ]; then
    echo "错误: 源目录 $SOURCE_DIR 不存在"
    exit 1
fi

# 创建备份目录
mkdir -p "$BACKUP_DIR"

# 执行备份
echo "开始备份 $SOURCE_DIR..."
tar -czf "$BACKUP_DIR/$BACKUP_FILE" -C "$(dirname "$SOURCE_DIR")" "$(basename "$SOURCE_DIR")"

if [ $? -eq 0 ]; then
    echo "备份成功: $BACKUP_DIR/$BACKUP_FILE"
    
    # 删除7天前的备份
    find "$BACKUP_DIR" -name "backup_*.tar.gz" -mtime +7 -delete
    echo "已清理7天前的备份文件"
else
    echo "备份失败"
    exit 1
fi

3. 日志分析脚本

访问日志分析

#!/bin/bash

# 日志分析脚本
LOG_FILE="/var/log/nginx/access.log"

if [ ! -f "$LOG_FILE" ]; then
    echo "日志文件不存在: $LOG_FILE"
    exit 1
fi

echo "=== Nginx访问日志分析 ==="
echo "分析文件: $LOG_FILE"
echo "分析时间: $(date)"
echo

echo "=== 总访问量 ==="
total_requests=$(wc -l < "$LOG_FILE")
echo "总请求数: $total_requests"

echo
echo "=== 状态码统计 ==="
awk '{print $9}' "$LOG_FILE" | sort | uniq -c | sort -nr

echo
echo "=== 访问量最高的IP ==="
awk '{print $1}' "$LOG_FILE" | sort | uniq -c | sort -nr | head -10

echo
echo "=== 访问量最高的页面 ==="
awk '{print $7}' "$LOG_FILE" | sort | uniq -c | sort -nr | head -10

调试和错误处理

1. 脚本调试

调试选项

#!/bin/bash

# 启用调试模式
set -x    # 显示执行的命令
set -e    # 遇到错误立即退出
set -u    # 使用未定义变量时报错

# 或在脚本开头使用
#!/bin/bash -x

# 调试特定部分
set -x
echo "这部分会显示调试信息"
set +x
echo "这部分不显示调试信息"

2. 错误处理

错误处理机制

#!/bin/bash

# 错误处理函数
error_exit() {
    echo "错误: $1" >&2
    exit 1
}

# 检查命令执行结果
backup_files() {
    cp important_file.txt backup/ || error_exit "备份失败"
    echo "备份成功"
}

# 使用trap捕获信号
cleanup() {
    echo "清理临时文件..."
    rm -f /tmp/temp_file
}

trap cleanup EXIT

# 脚本主体
echo "开始执行脚本..."
backup_files
echo "脚本执行完成"

总结

Shell脚本编程要点:

  1. 基础语法:掌握变量、条件判断、循环结构
  2. 函数使用:编写可重用的函数模块
  3. 输入输出:处理用户输入和文件操作
  4. 错误处理:实现健壮的错误处理机制
  5. 实际应用:编写系统监控、备份等实用脚本

下一课预告

在下一课中,我们将学习Web服务器配置,包括:

  • Apache服务器配置
  • Nginx服务器配置
  • SSL证书配置
  • 虚拟主机设置

💡 小贴士:Shell脚本是Linux系统管理的重要工具。建议从简单脚本开始练习,逐步掌握复杂的编程结构,多写多练才能熟练掌握。

Vue3 + TypeScript 企业级项目实战

课程推荐

Vue3 + TypeScript 企业级项目实战
Python 全栈开发工程师培训

热门课程

Python 全栈开发工程师培训

📚 文章对你有帮助?请关注我的公众号,万分感谢!

获取更多优质技术文章,第一时间掌握最新技术动态

关注公众号

关注公众号

第一时间获取最新技术文章

添加微信

添加微信

技术交流 · 问题答疑 · 学习指导

评论讨论

欢迎留下你的想法和建议