Shell 脚本编程 #

一、Shell 脚本基础 #

1.1 脚本结构 #

bash
#!/bin/bash
# 这是注释
echo "Hello World"

脚本组成:

  • Shebang#!/bin/bash 指定解释器
  • 注释:以 # 开头
  • 命令:可执行的 Shell 命令

1.2 执行脚本 #

bash
# 方法一:添加执行权限
chmod +x script.sh
./script.sh

# 方法二:指定解释器
bash script.sh
sh script.sh

# 方法三:source 执行(在当前 Shell 中执行)
source script.sh
. script.sh

# 方法四:重定向执行
bash < script.sh

1.3 脚本调试 #

bash
# 显示执行的命令
bash -x script.sh

# 检查语法错误
bash -n script.sh

# 显示详细信息
bash -v script.sh

# 在脚本中启用调试
#!/bin/bash
set -x    # 开启调试
set +x    # 关闭调试

二、变量 #

2.1 变量定义 #

bash
# 定义变量(等号两边不能有空格)
name="John"
age=25

# 使用变量
echo $name
echo ${name}
echo "My name is $name"

# 只读变量
readonly PI=3.14

# 删除变量
unset name

2.2 变量类型 #

bash
# 字符串
str="Hello World"
str='Hello World'    # 单引号不解析变量

# 数字
num=123

# 数组
arr=(1 2 3 4 5)
arr[0]=1
arr[1]=2

# 访问数组
echo ${arr[0]}       # 第一个元素
echo ${arr[@]}       # 所有元素
echo ${arr[*]}       # 所有元素
echo ${#arr[@]}      # 数组长度
echo ${#arr[*]}      # 数组长度

# 关联数组(需要 declare)
declare -A user
user[name]="John"
user[age]=25
echo ${user[name]}

2.3 特殊变量 #

bash
$0    # 脚本名称
$1-$9 # 第 1-9 个参数
${10} # 第 10 个参数
$#    # 参数个数
$@    # 所有参数(独立)
$*    # 所有参数(整体)
$?    # 上一个命令的退出状态
$$    # 当前进程 PID
$!    # 后台进程 PID
$-    # 当前 Shell 选项
$_    # 上一个命令的最后一个参数

2.4 环境变量 #

bash
# 查看环境变量
env
printenv

# 查看指定环境变量
echo $HOME
echo $PATH
echo $USER
echo $PWD

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

# 在脚本中使用
#!/bin/bash
export PATH="/usr/local/bin:$PATH"

2.5 变量替换 #

bash
# 默认值
echo ${var:-default}     # var 未定义或为空,返回 default
echo ${var:=default}     # var 未定义或为空,赋值为 default
echo ${var:+value}       # var 已定义且非空,返回 value
echo ${var:?error}       # var 未定义或为空,输出错误

# 字符串操作
str="Hello World"
echo ${#str}             # 字符串长度
echo ${str:0:5}          # 截取子串
echo ${str#Hello}        # 删除开头匹配
echo ${str%World}        # 删除结尾匹配
echo ${str/World/Linux}  # 替换第一个匹配
echo ${str//o/O}         # 替换所有匹配
echo ${str^}             # 首字母大写
echo ${str^^}            # 全部大写
echo ${str,}             # 首字母小写
echo ${str,,}            # 全部小写

三、输入输出 #

3.1 echo 输出 #

bash
# 基本输出
echo "Hello World"

# 不换行
echo -n "Hello"
echo " World"

# 解析转义字符
echo -e "Line1\nLine2"
echo -e "Tab\tSeparated"

# 输出到文件
echo "content" > file.txt
echo "content" >> file.txt

# 输出变量
name="John"
echo "Hello, $name"
echo 'Hello, $name'    # 单引号不解析变量

3.2 printf 格式化输出 #

bash
# 格式化输出
printf "Name: %s\n" "John"
printf "Age: %d\n" 25
printf "Price: %.2f\n" 99.99

# 格式说明符
%s    # 字符串
%d    # 整数
%f    # 浮点数
%x    # 十六进制
%o    # 八进制
%e    # 科学计数法

# 宽度和对齐
printf "%10s\n" "test"      # 右对齐,宽度 10
printf "%-10s\n" "test"     # 左对齐,宽度 10
printf "%.2f\n" 99.999      # 保留 2 位小数

3.3 read 输入 #

bash
# 基本输入
read name
echo "Hello, $name"

# 带提示
read -p "Enter your name: " name

# 静默输入(密码)
read -s -p "Enter password: " password

# 限制输入长度
read -n 1 -p "Continue? [y/n] " answer

# 超时
read -t 10 -p "Enter name (10s): " name

# 读取到数组
read -a arr -p "Enter numbers: "
echo ${arr[@]}

# 读取多行
while read line; do
    echo "Line: $line"
done < file.txt

四、运算 #

4.1 算术运算 #

bash
# 使用 $(( ))
a=10
b=3
echo $((a + b))    # 加
echo $((a - b))    # 减
echo $((a * b))    # 乘
echo $((a / b))    # 除
echo $((a % b))    # 取余
echo $((a ** b))   # 幂

# 使用 expr
expr 10 + 3
expr 10 - 3
expr 10 \* 3       # * 需要转义
expr 10 / 3
expr 10 % 3

# 使用 let
let a=10+3
let a++
let a--

# 使用 bc(支持浮点)
echo "10.5 + 3.2" | bc
echo "scale=2; 10/3" | bc

4.2 比较运算 #

bash
# 数字比较
-eq    # 等于
-ne    # 不等于
-gt    # 大于
-lt    # 小于
-ge    # 大于等于
-le    # 小于等于

# 示例
[ $a -eq $b ]
[[ $a -eq $b ]]
test $a -eq $b

4.3 字符串比较 #

bash
# 字符串比较
=      # 等于
!=     # 不等于
-z     # 长度为零
-n     # 长度非零

# 示例
[ "$str1" = "$str2" ]
[[ "$str1" == "$str2" ]]
[ -z "$str" ]
[ -n "$str" ]

4.4 文件测试 #

bash
# 文件测试
-e    # 文件存在
-f    # 是普通文件
-d    # 是目录
-r    # 可读
-w    # 可写
-x    # 可执行
-s    # 文件非空
-L    # 是符号链接

# 示例
[ -f file.txt ]
[ -d directory ]
[ -x script.sh ]
[ -r file.txt ] && [ -w file.txt ]

五、流程控制 #

5.1 if 条件 #

bash
# 基本 if
if [ condition ]; then
    commands
fi

# if-else
if [ condition ]; then
    commands1
else
    commands2
fi

# if-elif-else
if [ condition1 ]; then
    commands1
elif [ condition2 ]; then
    commands2
else
    commands3
fi

# 示例
if [ $age -ge 18 ]; then
    echo "Adult"
else
    echo "Minor"
fi

# 使用 [[ ]](推荐)
if [[ $str == "hello" ]]; then
    echo "Match"
fi

# 逻辑运算
if [ $a -gt 0 ] && [ $a -lt 10 ]; then
    echo "0 < a < 10"
fi

if [ $a -lt 0 ] || [ $a -gt 10 ]; then
    echo "a < 0 or a > 10"
fi

5.2 case 分支 #

bash
# case 语句
case $variable in
    pattern1)
        commands1
        ;;
    pattern2)
        commands2
        ;;
    *)
        default_commands
        ;;
esac

# 示例
case $1 in
    start)
        echo "Starting..."
        ;;
    stop)
        echo "Stopping..."
        ;;
    restart)
        echo "Restarting..."
        ;;
    *)
        echo "Usage: $0 {start|stop|restart}"
        ;;
esac

# 模式匹配
case $answer in
    [yY]|[yY][eE][sS])
        echo "Yes"
        ;;
    [nN]|[nN][oO])
        echo "No"
        ;;
    *)
        echo "Unknown"
        ;;
esac

5.3 for 循环 #

bash
# 列表循环
for item in item1 item2 item3; do
    echo $item
done

# 遍历文件
for file in *.txt; do
    echo $file
done

# 遍历命令输出
for user in $(cat /etc/passwd | cut -d: -f1); do
    echo $user
done

# C 风格循环
for ((i=0; i<10; i++)); do
    echo $i
done

# 遍历数组
arr=(1 2 3 4 5)
for num in ${arr[@]}; do
    echo $num
done

# 遍历参数
for arg in "$@"; do
    echo $arg
done

5.4 while 循环 #

bash
# 基本 while
while [ condition ]; do
    commands
done

# 示例
count=0
while [ $count -lt 10 ]; do
    echo $count
    ((count++))
done

# 读取文件
while read line; do
    echo $line
done < file.txt

# 无限循环
while true; do
    echo "Running..."
    sleep 1
done

# 按条件退出
while true; do
    read -p "Continue? (y/n) " answer
    [[ $answer == "n" ]] && break
done

5.5 until 循环 #

bash
# until 循环(条件为假时执行)
until [ condition ]; do
    commands
done

# 示例
count=0
until [ $count -ge 10 ]; do
    echo $count
    ((count++))
done

5.6 循环控制 #

bash
# break - 退出循环
for i in {1..10}; do
    if [ $i -eq 5 ]; then
        break
    fi
    echo $i
done

# continue - 跳过本次循环
for i in {1..10}; do
    if [ $i -eq 5 ]; then
        continue
    fi
    echo $i
done

# 嵌套循环
for i in {1..3}; do
    for j in {1..3}; do
        echo "$i, $j"
    done
done

六、函数 #

6.1 函数定义 #

bash
# 方式一
function_name() {
    commands
    return value
}

# 方式二
function function_name {
    commands
    return value
}

# 调用函数
function_name
function_name arg1 arg2

6.2 函数参数 #

bash
# 函数参数
greet() {
    echo "Hello, $1"
    echo "Age: $2"
}

greet "John" 25

# 所有参数
show_args() {
    echo "Arguments: $@"
    echo "Count: $#"
}

show_args a b c d

6.3 返回值 #

bash
# 使用 return
add() {
    return $(($1 + $2))
}

add 10 20
echo $?    # 返回值

# 使用 echo
add() {
    echo $(($1 + $2))
}

result=$(add 10 20)
echo $result

6.4 局部变量 #

bash
# 使用 local
func() {
    local var="local"
    echo $var
}

var="global"
func
echo $var    # 输出 global

6.5 递归函数 #

bash
# 阶乘
factorial() {
    if [ $1 -le 1 ]; then
        echo 1
    else
        local prev=$(factorial $(($1 - 1)))
        echo $(($1 * prev))
    fi
}

result=$(factorial 5)
echo $result    # 输出 120

七、实战脚本示例 #

7.1 系统备份脚本 #

bash
#!/bin/bash
# 系统备份脚本

BACKUP_DIR="/backup"
DATE=$(date +%Y%m%d)
BACKUP_FILE="backup_$DATE.tar.gz"

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

# 备份
tar -czf $BACKUP_DIR/$BACKUP_FILE /home /etc

# 删除 7 天前的备份
find $BACKUP_DIR -name "backup_*.tar.gz" -mtime +7 -delete

echo "Backup completed: $BACKUP_FILE"

7.2 日志分析脚本 #

bash
#!/bin/bash
# 日志分析脚本

LOG_FILE="/var/log/nginx/access.log"

echo "=== 访问量 Top 10 IP ==="
awk '{print $1}' $LOG_FILE | sort | uniq -c | sort -rn | head -10

echo "=== 访问量 Top 10 URL ==="
awk '{print $7}' $LOG_FILE | sort | uniq -c | sort -rn | head -10

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

7.3 服务监控脚本 #

bash
#!/bin/bash
# 服务监控脚本

SERVICES=("nginx" "mysql" "redis")

for service in ${SERVICES[@]}; do
    if systemctl is-active --quiet $service; then
        echo "$service is running"
    else
        echo "$service is stopped"
        systemctl start $service
        echo "$service has been started"
    fi
done

7.4 批量用户创建脚本 #

bash
#!/bin/bash
# 批量创建用户

USER_FILE="users.txt"

if [ ! -f $USER_FILE ]; then
    echo "File $USER_FILE not found"
    exit 1
fi

while read username; do
    if id $username &>/dev/null; then
        echo "User $username already exists"
    else
        useradd -m -s /bin/bash $username
        echo "$username:password" | chpasswd
        echo "User $username created"
    fi
done < $USER_FILE

八、小结 #

本章学习了 Shell 脚本编程的基础知识,包括变量、流程控制、函数和实战脚本。

关键要点:

  1. Shebang 指定脚本解释器
  2. 变量无需声明,直接赋值
  3. 使用 [[ ]] 进行条件测试
  4. 函数使用 local 定义局部变量
  5. 脚本要有良好的注释和错误处理

下一章预告: 系统监控 - 学习系统性能监控和资源管理。

最后更新:2026-03-27