Shell脚本高级技巧#

Shell脚本是Linux系统管理和自动化的强大工具,掌握高级技巧可以让您编写更加高效、灵活和强大的脚本。本教程将详细介绍Shell脚本中的各种高级技巧,从正则表达式到进程管理,从数组操作到信号处理。

入门#

正则表达式基础#

正则表达式是用于匹配字符串模式的强大工具,在Shell脚本中广泛应用于文本处理。

# 基本正则表达式
# 匹配包含"test"的行
grep "test" file.txt

# 匹配以"test"开头的行
grep "^test" file.txt

# 匹配以"test"结尾的行
grep "test$" file.txt

# 匹配包含数字的行
grep "[0-9]" file.txt

# 匹配包含字母的行
grep "[a-zA-Z]" file.txt

# 匹配包含单词"test"的行(边界匹配)
grep "\\<test\\>" file.txt

数组基础#

数组是Shell中存储多个值的变量类型,支持索引数组和关联数组。

# 索引数组定义
fruits=(apple banana cherry date)

# 访问数组元素
echo "第一个元素: ${fruits[0]}"
echo "第二个元素: ${fruits[1]}"

# 访问所有元素
echo "所有元素: ${fruits[@]}"
echo "所有元素: ${fruits[*]}"

# 获取数组长度
echo "数组长度: ${#fruits[@]}"

# 修改数组元素
fruits[2]="grape"
echo "修改后: ${fruits[@]}"

# 添加元素
fruits+=(elderberry)
echo "添加后: ${fruits[@]}"

进程管理基础#

# 查看当前进程
ps

# 查看所有进程
ps aux

# 查看特定进程
ps aux | grep "bash"

# 终止进程
kill PID

# 强制终止进程
kill -9 PID

# 后台执行命令
command &

# 查看后台进程
jobs

# 将后台进程调回前台
fg %1

# 暂停进程
Ctrl+Z

# 继续执行暂停的进程
bg %1

中级#

高级正则表达式#

扩展正则表达式#

# 使用扩展正则表达式(-E选项)
# 匹配"test"或"exam"
grep -E "test|exam" file.txt

# 匹配重复字符
grep -E "a{2,3}" file.txt  # 匹配2-3个连续的a

# 匹配分组
grep -E "(test){2}" file.txt  # 匹配"testtest"

# 匹配可选字符
grep -E "colou?r" file.txt  # 匹配"color"或"colour"

# 匹配单词边界
grep -E "\\btest\\b" file.txt  # 匹配单词"test"

正则表达式工具#

# sed命令使用正则表达式
# 替换文本
sed 's/old/new/g' file.txt

# 替换并直接修改文件
sed -i 's/old/new/g' file.txt

# awk命令使用正则表达式
# 按模式匹配行
awk '/test/ {print}' file.txt

# 按模式分割字段
awk -F'[0-9]+' '{print $1}' file.txt

# 正则表达式匹配测试
grep -q "pattern" file.txt && echo "匹配" || echo "不匹配"

高级数组操作#

关联数组#

# 定义关联数组
declare -A colors

# 添加元素
colors["red"]="#FF0000"
colors["green"]="#00FF00"
colors["blue"]="#0000FF"

# 访问元素
echo "红色: ${colors["red"]}"
echo "绿色: ${colors[green]}"

# 遍历关联数组
for color in "${!colors[@]}"; do
    echo "$color: ${colors[$color]}"
done

# 获取关联数组长度
echo "数组长度: ${#colors[@]}"

数组操作#

# 数组切片
numbers=(1 2 3 4 5 6 7 8 9 10)
echo "前3个元素: ${numbers[@]:0:3}"  # 从索引0开始,取3个元素
echo "从索引2开始: ${numbers[@]:2}"    # 从索引2开始,取所有元素

# 数组排序
sorted=($(echo "${numbers[@]}" | tr ' ' '\n' | sort -n))
echo "排序后: ${sorted[@]}"

# 数组去重
unique=($(echo "${numbers[@]}" | tr ' ' '\n' | uniq))
echo "去重后: ${unique[@]}"

# 数组交集
array1=(1 2 3 4 5)
array2=(3 4 5 6 7)
intersection=($(comm -12 <(echo "${array1[@]}" | tr ' ' '\n' | sort) <(echo "${array2[@]}" | tr ' ' '\n' | sort)))
echo "交集: ${intersection[@]}"

# 数组差集
difference=($(comm -23 <(echo "${array1[@]}" | tr ' ' '\n' | sort) <(echo "${array2[@]}" | tr ' ' '\n' | sort)))
echo "差集: ${difference[@]}"

信号处理#

# 基本信号处理
trap 'echo "收到SIGINT信号"' SIGINT

echo "按Ctrl+C测试信号处理"
sleep 10

# 多个信号处理
trap 'echo "收到信号"' SIGINT SIGTERM

# 忽略信号
trap '' SIGINT

echo "按Ctrl+C不会终止脚本"
sleep 10

# 恢复默认信号处理
trap - SIGINT

echo "按Ctrl+C会终止脚本"
sleep 10

# 退出时执行清理
trap 'echo "执行清理"; rm -f temp.txt' EXIT

echo "创建临时文件"
touch temp.txt
sleep 5

高级#

进程控制#

进程创建和管理#

# 创建子进程
(child_process) {
    echo "子进程PID: $$"
    echo "父进程PID: $PPID"
    sleep 2
    echo "子进程完成"
}

child_process &
parent_pid=$$
echo "父进程PID: $parent_pid"
wait

echo "父进程完成"

# 进程替换
# 同时执行多个命令并捕获输出
paste <(ls *.txt) <(wc -l *.txt | grep -v total)

# 进程间通信
# 使用管道
echo "Hello" | (read msg; echo "收到: $msg")

# 使用命名管道
mkfifo mypipe

echo "写入命名管道" > mypipe &
echo "从命名管道读取: $(cat mypipe)"
rm mypipe

进程监控#

# 监控进程状态
watch -n 1 "ps aux | grep 'python'"

# 监控系统负载
watch -n 1 "uptime"

# 监控磁盘I/O
iotop

# 监控网络连接
netstat -tuln

# 监控进程资源使用
top -p PID

# 进程跟踪
strace command

高级文本处理#

awk 高级用法#

# awk 基本用法
# 打印文件的第一列
awk '{print $1}' file.txt

# 打印包含"test"的行的第一列
awk '/test/ {print $1}' file.txt

# 使用变量
awk '{sum += $1} END {print sum}' file.txt

# 使用自定义分隔符
awk -F"," '{print $1}' csv_file.txt

# 条件判断
awk '$1 > 10 {print $0}' file.txt

# 循环
awk '{
    for (i=1; i<=NF; i++) {
        print "字段" i ":" $i
    }
}' file.txt

# 数组
awk '{
    count[$1]++
} END {
    for (word in count) {
        print word ":" count[word]
    }
}' file.txt

sed 高级用法#

# sed 基本用法
# 替换第一处匹配
sed 's/old/new/' file.txt

# 替换所有匹配
sed 's/old/new/g' file.txt

# 替换第n处匹配
sed 's/old/new/2' file.txt

# 使用不同分隔符
sed 's|old|new|g' file.txt

# 删除空行
sed '/^$/d' file.txt

# 删除包含"test"的行
sed '/test/d' file.txt

# 在指定行后插入文本
sed '/test/a new line' file.txt

# 在指定行前插入文本
sed '/test/i new line' file.txt

# 替换指定行范围
sed '1,5s/old/new/g' file.txt

系统编程#

文件描述符操作#

# 基本文件描述符
# 0: 标准输入
# 1: 标准输出
# 2: 标准错误

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

# 自定义文件描述符
# 创建文件描述符3指向文件
exec 3> output.txt
echo "通过文件描述符3输出" >&3
exec 3>&-

# 读写文件描述符
exec 4<> data.txt
echo "写入数据" >&4
seek 0 4
cat <&4
exec 4>&-

# 复制文件描述符
exec 5>&1
echo "输出到标准输出" >&5
exec 5>&-

环境变量#

# 查看环境变量
env

# 查看特定环境变量
echo $HOME
echo $PATH

# 设置环境变量
export MY_VAR="value"
echo $MY_VAR

# 临时修改环境变量
PATH=/usr/local/bin:$PATH command

# 移除环境变量
unset MY_VAR

# 环境变量持久化
# 在~/.bashrc或~/.bash_profile中添加
export MY_VAR="value"

# 加载环境变量
source ~/.bashrc

大师#

高级脚本设计#

模块化脚本#

# 模块定义
# utils.sh

#!/bin/bash

# 日志函数
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $@"
}

# 文件检查函数
check_file() {
    local file=$1
    [ -f "$file" ] && [ -r "$file" ]
}

# 导出函数
export -f log
export -f check_file

# 主脚本

#!/bin/bash

# 导入模块
source utils.sh

# 使用模块函数
log "开始执行脚本"

if check_file "data.txt"; then
    log "数据文件存在"
else
    log "数据文件不存在"
fi

log "脚本执行完成"

配置管理#

# 配置文件
# config.sh

#!/bin/bash

# 默认配置
CONFIG_DIR="/etc/myapp"
LOG_FILE="/var/log/myapp.log"
DEBUG=false

# 加载用户配置
if [ -f "~/.myapp/config.sh" ]; then
    source "~/.myapp/config.sh"
fi

# 命令行参数覆盖
while getopts "d:l:v" opt; do
    case $opt in
        d)
            CONFIG_DIR=$OPTARG
            ;;
        l)
            LOG_FILE=$OPTARG
            ;;
        v)
            DEBUG=true
            ;;
    esac
done

# 主脚本

#!/bin/bash

# 加载配置
source config.sh

# 使用配置
echo "配置目录: $CONFIG_DIR"
echo "日志文件: $LOG_FILE"
echo "调试模式: $DEBUG"

性能优化#

脚本性能优化#

# 减少命令执行次数
# 不好的做法
for file in *.txt; do
    wc -l "$file"
done

# 好的做法
wc -l *.txt

# 减少文件I/O
# 不好的做法
for i in {1..1000}; do
    echo "$i" >> output.txt
done

# 好的做法
{
    for i in {1..1000}; do
        echo "$i"
    done
} > output.txt

# 减少子shell
# 不好的做法
for i in $(seq 1 1000); do
    echo $i
done

# 好的做法
for i in {1..1000}; do
    echo $i
done

# 使用更快的命令
# 不好的做法
ps aux | grep "bash" | wc -l

# 好的做法
pgrep -c "bash"

# 并行执行
# 不好的做法
for file in *.txt; do
    process_file "$file"
done

# 好的做法
find . -name "*.txt" | xargs -P 4 -I {} process_file "{}"

内存管理#

# 减少内存使用
# 处理大文件时使用流式处理
# 不好的做法
content=$(cat large_file.txt)
process "$content"

# 好的做法
cat large_file.txt | process

# 释放变量
large_variable="big content"
# 使用变量
unset large_variable

# 限制命令内存使用
ulimit -m 1024000  # 限制为1GB
memory_intensive_command

# 监控内存使用
while true; do
    mem_usage=$(ps -o rss= -p $$)
    echo "当前内存使用: $mem_usage KB"
    sleep 1
done

安全编程#

安全实践#

# 输入验证
validate_input() {
    local input=$1
    # 只允许字母和数字
    if [[ ! "$input" =~ ^[a-zA-Z0-9]+$ ]]; then
        echo "错误: 输入包含非法字符"
        return 1
    fi
    return 0
}

# 命令注入防护
safe_exec() {
    local cmd=$1
    # 检查命令是否安全
    if [[ "$cmd" =~ [;&|<>] ]]; then
        echo "错误: 命令包含非法字符"
        return 1
    fi
    eval "$cmd"
}

# 路径遍历防护
safe_path() {
    local path=$1
    # 解析绝对路径
    real_path=$(realpath "$path")
    # 检查是否在允许的目录内
    if [[ ! "$real_path" =~ ^/allowed/dir ]]; then
        echo "错误: 路径超出允许范围"
        return 1
    fi
    echo "$real_path"
}

# 权限检查
check_permissions() {
    if [ $(id -u) -ne 0 ]; then
        echo "错误: 需要root权限"
        return 1
    fi
    return 0
}

加密和哈希#

# 生成随机密码
openssl rand -base64 12

# 加密文件
openssl enc -aes-256-cbc -salt -in plaintext.txt -out encrypted.txt

# 解密文件
openssl enc -d -aes-256-cbc -in encrypted.txt -out decrypted.txt

# 生成文件哈希
md5sum file.txt
sha256sum file.txt

# 密码哈希
echo "password" | sha256sum

# 验证哈希
if [ "$(echo "password" | sha256sum)" = "expected_hash" ]; then
    echo "密码正确"
else
    echo "密码错误"
fi

无敌#

高级系统集成#

网络编程#

# 基本网络操作
# 检查网络连接
ping -c 1 google.com

# 检查端口
nc -z google.com 80

# 简单HTTP请求
curl http://example.com

# 下载文件
wget http://example.com/file.txt

# 创建简单的HTTP服务器
python3 -m http.server 8080

# 网络套接字
# 使用nc创建服务器
nc -l 8080

# 使用nc连接服务器
nc localhost 8080

# 传输文件
# 接收端
nc -l 8080 > received_file.txt

# 发送端
nc localhost 8080 < send_file.txt

数据库操作#

# SQLite操作
# 创建数据库
sqlite3 mydb.db "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT);"

# 插入数据
sqlite3 mydb.db "INSERT INTO users (name, email) VALUES ('John', 'john@example.com');"

# 查询数据
sqlite3 mydb.db "SELECT * FROM users;"

# MySQL/MariaDB操作
# 连接数据库
mysql -u user -p database

# 执行SQL命令
mysql -u user -p database -e "SELECT * FROM users;"

# PostgreSQL操作
# 连接数据库
psql -U user -d database

# 执行SQL命令
psql -U user -d database -c "SELECT * FROM users;"

高级工具集成#

Git 集成#

# Git 基本操作
git status
git add .
git commit -m "提交消息"
git push

# Git 分支
git branch
git checkout branch_name
git merge branch_name

# Git 日志
git log
git log --oneline

# Git 标签
git tag v1.0
git push --tags

# Git 钩子
# .git/hooks/pre-commit

#!/bin/bash

# 运行测试
echo "运行测试..."
if ! ./run_tests.sh; then
    echo "测试失败,取消提交"
    exit 1
fi

echo "测试通过,继续提交"
exit 0

Docker 集成#

# Docker 基本操作
docker ps
docker images
docker run -it ubuntu bash

# Docker 构建
docker build -t myapp .

# Docker 运行
docker run -d -p 8080:80 myapp

# Docker Compose
docker-compose up -d
docker-compose down

# Docker 脚本
# docker_deploy.sh

#!/bin/bash

# 构建镜像
docker build -t myapp:$(date +%Y%m%d) .

# 停止旧容器
docker stop myapp || true
docker rm myapp || true

# 运行新容器
docker run -d --name myapp -p 8080:80 myapp:$(date +%Y%m%d)

echo "部署完成"

实用脚本示例#

系统管理工具#

#!/bin/bash

# 系统管理工具

# 显示系统信息
show_system_info() {
    echo "===== 系统信息 ====="
uname -a
echo ""

    echo "===== 硬件信息 ====="
lscpu
echo ""

    echo "===== 内存信息 ====="
free -h
echo ""

    echo "===== 磁盘信息 ====="
df -h
echo ""

    echo "===== 网络信息 ====="
ip addr
echo ""
}

# 服务管理
manage_service() {
    local action=$1
    local service=$2
    
    case $action in
        start)
            sudo systemctl start "$service"
            ;;
        stop)
            sudo systemctl stop "$service"
            ;;
        restart)
            sudo systemctl restart "$service"
            ;;
        status)
            sudo systemctl status "$service"
            ;;
        *)
            echo "无效操作: $action"
            return 1
            ;;
    esac
}

# 系统监控
monitor_system() {
    local interval=5
    local count=10
    
    for ((i=1; i<=count; i++)); do
        clear
        echo "===== 系统监控 (第$i/$count次) ====="
        
        echo "CPU使用:"
top -bn1 | grep "Cpu(s)"
        
        echo "\n内存使用:"
        free -h
        
        echo "\n磁盘I/O:"
iostat
        
        echo "\n网络流量:"
        netstat -i
        
        if [ $i -lt $count ]; then
            sleep $interval
        fi
    done
}

# 主菜单
show_menu() {
    echo "===== 系统管理工具 ====="
    echo "1. 显示系统信息"
    echo "2. 管理服务"
    echo "3. 系统监控"
    echo "4. 退出"
    echo "===================="
}

# 主循环
while true; do
    show_menu
    read -p "请选择: " choice
    
    case $choice in
        1)
            show_system_info
            ;;
        2)
            read -p "输入操作(start/stop/restart/status): " action
            read -p "输入服务名: " service
            manage_service "$action" "$service"
            ;;
        3)
            monitor_system
            ;;
        4)
            echo "再见!"
            exit 0
            ;;
        *)
            echo "无效选择"
            ;;
    esac
    
    echo "按Enter键继续..."
    read
done

数据处理工具#

#!/bin/bash

# 数据处理工具

# CSV文件处理
process_csv() {
    local file=$1
    
    echo "===== CSV文件处理 ====="
    echo "文件: $file"
    echo "行数: $(wc -l < "$file")"
    echo ""
    
    # 显示前5行
    echo "前5行:"
    head -5 "$file"
    echo ""
    
    # 统计列数
    columns=$(head -1 "$file" | tr ',' '\n' | wc -l)
    echo "列数: $columns"
    echo ""
    
    # 显示列名
    echo "列名:"
    head -1 "$file" | tr ',' '\n' | nl
    echo ""
    
    # 按列排序
    read -p "输入排序列号: " sort_col
    echo "按列$sort_col排序:"
    sort -t',' -k"$sort_col" "$file" | head -10
}

# 日志分析
analyze_log() {
    local file=$1
    
    echo "===== 日志分析 ====="
    echo "文件: $file"
    echo "行数: $(wc -l < "$file")"
    echo ""
    
    # 统计错误数
    error_count=$(grep -c "ERROR" "$file")
    echo "错误数: $error_count"
    echo ""
    
    # 统计警告数
    warning_count=$(grep -c "WARNING" "$file")
    echo "警告数: $warning_count"
    echo ""
    
    # 显示最近的错误
    echo "最近的错误:"
    grep "ERROR" "$file" | tail -10
    echo ""
    
    # 显示最近的警告
    echo "最近的警告:"
    grep "WARNING" "$file" | tail -10
}

# 文本转换
convert_text() {
    local file=$1
    local operation=$2
    
    case $operation in
        upper)
            echo "转换为大写:"
            tr '[:lower:]' '[:upper:]' < "$file" | head -10
            ;;
        lower)
            echo "转换为小写:"
            tr '[:upper:]' '[:lower:]' < "$file" | head -10
            ;;
        title)
            echo "转换为标题格式:"
            sed 's/\b\(\w\)/\U\1/g' < "$file" | head -10
            ;;
        reverse)
            echo "反转每行:"
            rev < "$file" | head -10
            ;;
        *)
            echo "无效操作"
            ;;
    esac
}

# 主菜单
show_menu() {
    echo "===== 数据处理工具 ====="
    echo "1. CSV文件处理"
    echo "2. 日志分析"
    echo "3. 文本转换"
    echo "4. 退出"
    echo "===================="
}

# 主循环
while true; do
    show_menu
    read -p "请选择: " choice
    
    case $choice in
        1)
            read -p "输入CSV文件路径: " csv_file
            if [ -f "$csv_file" ]; then
                process_csv "$csv_file"
            else
                echo "文件不存在"
            fi
            ;;
        2)
            read -p "输入日志文件路径: " log_file
            if [ -f "$log_file" ]; then
                analyze_log "$log_file"
            else
                echo "文件不存在"
            fi
            ;;
        3)
            read -p "输入文本文件路径: " text_file
            if [ -f "$text_file" ]; then
                echo "1. 转换为大写"
                echo "2. 转换为小写"
                echo "3. 转换为标题格式"
                echo "4. 反转每行"
                read -p "请选择操作: " op_choice
                
                case $op_choice in
                    1)
                        convert_text "$text_file" upper
                        ;;
                    2)
                        convert_text "$text_file" lower
                        ;;
                    3)
                        convert_text "$text_file" title
                        ;;
                    4)
                        convert_text "$text_file" reverse
                        ;;
                    *)
                        echo "无效选择"
                        ;;
                esac
            else
                echo "文件不存在"
            fi
            ;;
        4)
            echo "再见!"
            exit 0
            ;;
        *)
            echo "无效选择"
            ;;
    esac
    
    echo "按Enter键继续..."
    read
done

总结#

Shell脚本高级技巧是提升脚本编写能力的关键,通过掌握正则表达式、数组操作、进程管理、信号处理等高级技术,您可以编写更加高效、灵活和强大的Shell脚本。从简单的文本处理到复杂的系统管理,Shell脚本都能胜任。

本教程涵盖了Shell脚本的各种高级技巧,包括正则表达式、数组操作、进程管理、信号处理、模块化设计、性能优化和安全编程等内容。通过学习和实践这些技巧,您将能够编写更加专业、可靠的Shell脚本,解决各种复杂的系统管理和自动化任务。

记住,Shell脚本的威力在于其简洁性和灵活性,结合Linux系统的各种工具和命令,可以实现几乎无限的可能性。不断学习和实践,您将成为Shell脚本的大师。