Shell 脚本核心教程(Bash 为主)

本文档聚焦 Linux 系统中最常用的 Bash Shell,整理了 Shell 脚本的基础语法、核心命令、流程控制、实战技巧和常用示例,适合新手快速入门并能解决日常运维场景需求。

一、Shell 基础

1. 什么是 Shell

Shell 是操作系统的命令解释器,是用户与 Linux 内核交互的桥梁。Bash(Bourne-Again Shell)是绝大多数 Linux 发行版的默认 Shell。

2. 第一个 Shell 脚本

(1)创建脚本文件

touch hello.sh  # 创建脚本文件
vim hello.sh    # 编辑脚本

(2)脚本内容

#!/bin/bash
# 这是注释:第一个 Shell 脚本
echo "Hello, Shell!"  # 输出内容到控制台

(3)执行脚本的3种方式

# 方式1:赋予执行权限后直接运行(推荐)
chmod +x hello.sh
./hello.sh

# 方式2:通过 bash 命令执行(无需执行权限)
bash hello.sh

# 方式3:在当前 Shell 环境执行(会影响当前环境,慎用)
source hello.sh
# 或简写
. hello.sh

3. 脚本头部声明(Shebang)

  • #!/bin/bash:指定使用 Bash 解释器(最常用)
  • #!/bin/sh:指定使用系统默认 Shell(通常是 Bash 的软链接)
  • #!/usr/bin/env bash:自动查找系统中的 Bash 路径(跨平台友好)

二、Shell 变量

1. 变量定义与使用

(1)基本规则

  • 变量名只能包含字母、数字、下划线,且不能以数字开头;
  • 赋值时 = 两边无空格
  • 使用变量需加 $,推荐用 ${变量名} 避免歧义。

(2)示例

#!/bin/bash
# 定义普通变量
name="张三"
age=25
readonly PI=3.14159  # 只读变量(不可修改)
unset age            # 删除变量(只读变量无法删除)

# 使用变量
echo "姓名:$name"
echo "年龄:${age}"  # 推荐写法,避免和后续字符混淆
echo "圆周率:${PI}"

2. 特殊变量(内置变量)

变量 说明 示例
$0 脚本文件名 echo "脚本名:$0"
$1-$9 脚本的位置参数(第1-9个参数) 执行 ./test.sh 10 20 时,$1=10$2=20
${10} 第10个及以上参数(必须加花括号) -
$# 位置参数的个数 echo "参数个数:$#"
$* 所有位置参数(视为一个整体) -
$@ 所有位置参数(视为独立个体) -
$? 上一条命令的退出状态码(0=成功,非0=失败) echo "上条命令执行结果:$?"
$$ 当前脚本的进程ID(PID) echo "脚本PID:$$"
$! 最后一个后台进程的PID -

示例:位置参数使用

#!/bin/bash
echo "脚本名:$0"
echo "第1个参数:$1"
echo "第2个参数:$2"
echo "参数总数:$#"
echo "所有参数:$@"

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

执行:./param.sh 苹果 香蕉 橘子,输出:

脚本名:./param.sh
第1个参数:苹果
第2个参数:香蕉
参数总数:3
所有参数:苹果 香蕉 橘子
参数:苹果
参数:香蕉
参数:橘子

3. 环境变量

  • 查看所有环境变量:envprintenv

  • 常用环境变量:

    • PATH:命令搜索路径(如 /usr/bin:/bin
    • HOME:当前用户主目录
    • PWD:当前工作目录
    • USER:当前用户名
    • SHELL:当前使用的 Shell
  • 设置环境变量(临时生效):

    export PATH=$PATH:/usr/local/mybin  # 添加自定义命令路径
  • 永久生效:将上述命令写入 ~/.bashrc(当前用户)或 /etc/profile(所有用户),执行 source ~/.bashrc 生效。

三、字符串操作

1. 字符串定义

str1='单引号字符串'  # 单引号:原样输出,不解析变量和转义字符
str2="双引号字符串:${name}"  # 双引号:解析变量和转义字符
str3=无引号字符串  # 无引号:和双引号类似,但不能包含空格

2. 核心操作

操作 语法 示例
获取长度 ${#字符串} len=${#str2}; echo $len
截取子串 ${字符串:起始位置:长度} str="hello world"; echo ${str:0:5}hello
替换 ${字符串/旧值/新值}(替换第一个) ${str/world/shell}hello shell
替换 ${字符串//旧值/新值}(替换所有) ${str/l/L}heLLo worLd
删除前缀 ${字符串#匹配规则}(最短匹配) str="a1b1c1"; echo ${str#*1}b1c1
删除前缀 ${字符串##匹配规则}(最长匹配) ${str##*1}c1
删除后缀 ${字符串%匹配规则}(最短匹配) ${str%1*}a1b
删除后缀 ${字符串%%匹配规则}(最长匹配) ${str%%1*}a

四、流程控制

1. 条件判断(if)

(1)基本语法

# 单分支
if [ 条件表达式 ]; then
    执行语句
fi

# 双分支
if [ 条件表达式 ]; then
    执行语句1
else
    执行语句2
fi

# 多分支
if [ 条件表达式1 ]; then
    执行语句1
elif [ 条件表达式2 ]; then
    执行语句2
else
    执行语句3
fi

(2)条件表达式(常用)

类型 语法 说明
文件判断 -f 文件 判断是否为普通文件
-d 文件 判断是否为目录
-e 文件 判断文件/目录是否存在
-r 文件 判断是否有读权限
-w 文件 判断是否有写权限
-x 文件 判断是否有执行权限
字符串判断 [ "$str1" = "$str2" ] 判断字符串相等(= 两边有空格)
[ "$str1" != "$str2" ] 判断字符串不相等
[ -z "$str" ] 判断字符串为空
[ -n "$str" ] 判断字符串非空
数值判断 [ $num1 -eq $num2 ] 等于(equal)
[ $num1 -ne $num2 ] 不等于(not equal)
[ $num1 -gt $num2 ] 大于(greater than)
[ $num1 -lt $num2 ] 小于(less than)
[ $num1 -ge $num2 ] 大于等于(greater equal)
[ $num1 -le $num2 ] 小于等于(less equal)
逻辑判断 [ 条件1 ] && [ 条件2 ] 且(两个条件都成立)
[ 条件1 ] || [ 条件2 ] 或(任一条件成立)
! [ 条件 ] 非(条件不成立)

示例:文件存在判断

#!/bin/bash
file="/etc/passwd"
if [ -f "$file" ]; then
    echo "$file 是普通文件"
elif [ -d "$file" ]; then
    echo "$file 是目录"
else
    echo "$file 不存在"
fi

2. 分支选择(case)

适合多值匹配场景,替代多层 if-elif。

#!/bin/bash
read -p "请输入数字(1-3):" num
case $num in
    1)
        echo "你输入了1"
        ;;
    2)
        echo "你输入了2"
        ;;
    3)
        echo "你输入了3"
        ;;
    *)
        echo "输入无效,请输入1-3的数字"
        ;;
esac

3. 循环

(1)for 循环

# 方式1:遍历列表
for i in 1 2 3 4 5; do
    echo "数字:$i"
done

# 方式2:遍历文件
for file in /root/*; do
    echo "文件:$file"
done

# 方式3:C语言风格
for ((i=0; i<5; i++)); do
    echo "循环次数:$i"
done

(2)while 循环(条件为真时执行)

#!/bin/bash
i=1
while [ $i -le 5 ]; do
    echo "当前值:$i"
    i=$((i+1))  # 数值自增
done

(3)until 循环(条件为假时执行)

#!/bin/bash
i=1
until [ $i -gt 5 ]; do
    echo "当前值:$i"
    i=$((i+1))
done

(4)循环控制

  • break:跳出当前循环
  • continue:跳过本次循环,继续下一次
#!/bin/bash
for i in 1 2 3 4 5; do
    if [ $i -eq 3 ]; then
        continue  # 跳过3
    fi
    if [ $i -eq 5 ]; then
        break  # 跳出循环
    fi
    echo "数字:$i"
done

五、函数

1. 函数定义与调用

#!/bin/bash
# 定义函数
say_hello() {
    local name=$1  # local 定义局部变量(仅函数内有效)
    echo "你好,$name!"
    return 0  # 返回值(0-255,默认0为成功)
}

# 调用函数
say_hello "李四"
echo "函数返回值:$?"  # 获取返回值

2. 函数参数

函数内使用 $1-$9$#$@ 等特殊变量获取参数,和脚本参数规则一致。

#!/bin/bash
# 计算两个数的和
add() {
    local num1=$1
    local num2=$2
    sum=$((num1 + num2))
    echo "和为:$sum"
}

add 10 20  # 调用:输出 和为:30

六、常用工具/命令

1. 输入输出

  • echo:输出内容(echo -e 解析转义字符,如 echo -e "hello\nworld"
  • read:读取用户输入(read -p "提示语:" 变量名
  • printf:格式化输出(类似 C 语言,printf "姓名:%s,年龄:%d\n" "$name" "$age"

2. 文本处理

  • grep:过滤文本(grep "error" log.txt 查找包含error的行)
  • sed:流编辑(sed 's/old/new/g' file.txt 替换文件中所有old为new)
  • awk:文本分析(awk '{print $1}' file.txt 输出文件第一列)
  • cut:截取列(cut -d ":" -f 1 /etc/passwd 以:分隔,截取第一列)

3. 其他常用

  • date:日期时间(date "+%Y-%m-%d %H:%M:%S" 输出 2025-12-31 10:00:00)
  • mkdir/rm:创建/删除目录/文件
  • cp/mv:复制/移动文件
  • find:查找文件(find /root -name "*.sh" 查找root下所有.sh文件)
  • wc:统计(wc -l file.txt 统计文件行数)

七、实战示例

示例1:批量创建用户

#!/bin/bash
# 批量创建用户,密码统一为 123456
users=("user1" "user2" "user3")
for user in "${users[@]}"; do
    # 判断用户是否存在
    if id "$user" &>/dev/null; then
        echo "$user 已存在,跳过"
    else
        useradd "$user"
        echo "123456" | passwd --stdin "$user" &>/dev/null
        echo "$user 创建成功"
    fi
done

示例2:日志清理脚本

#!/bin/bash
# 清理7天前的日志文件
log_dir="/var/log/myapp"
if [ -d "$log_dir" ]; then
    # 查找7天前的.log文件并删除
    find "$log_dir" -name "*.log" -mtime +7 -exec rm -f {} \;
    echo "$(date +%Y-%m-%d):已清理 $log_dir 下7天前的日志" >> /var/log/clean_log.log
else
    echo "日志目录 $log_dir 不存在"
    exit 1
fi

示例3:监控进程是否运行

#!/bin/bash
# 监控nginx进程,若未运行则启动
process="nginx"
if ! pgrep "$process" &>/dev/null; then
    echo "$(date):$process 未运行,启动中..." >> /var/log/process_monitor.log
    systemctl start nginx
    # 检查启动是否成功
    if [ $? -eq 0 ]; then
        echo "$(date):$process 启动成功" >> /var/log/process_monitor.log
    else
        echo "$(date):$process 启动失败" >> /var/log/process_monitor.log
    fi
else
    echo "$(date):$process 运行正常" >> /var/log/process_monitor.log
fi

八、脚本调试与规范

1. 脚本调试

  • 执行时调试:bash -x 脚本名.sh(显示每一行执行的命令和结果)
  • 脚本内调试:在脚本头部添加 set -x(开启调试),set +x(关闭调试)

2. 编写规范

  • 脚本头部添加注释(功能、作者、创建时间、参数说明);
  • 变量名、函数名使用小写+下划线(如 log_dirclean_log);
  • 关键操作添加日志输出,便于排查问题;
  • 对文件/目录、权限等做前置判断,避免脚本异常退出;
  • 避免使用绝对路径(除非必要),或通过变量定义路径。

总结

  1. Shell 脚本核心语法:变量定义 → 流程控制(if/case/循环)→ 函数 → 命令调用;
  2. 条件判断是重点:牢记文件、字符串、数值的判断语法,注意 [] 内的空格;
  3. 实战技巧:善用 grep/sed/awk 处理文本,find 查找文件,date 处理时间;
  4. 调试与规范:用 bash -x 调试,遵循命名和注释规范,提升脚本可维护性。

如需查看具体命令的用法,执行 命令 --help(如 grep --help)或 man 命令(如 man bash)查看详细手册。