Shell脚本(.sh)常用语法全解析:从入门到实战

文章目录

一、脚本基础结构

1. 解释器声明(Shebang)

脚本首行必须指定解释器,告诉系统用哪个程序执行:

bash 复制代码
#!/bin/bash          # 最常用,指定Bash解释器
#!/usr/bin/env bash  # 跨平台兼容写法(推荐)
#!/bin/sh            # POSIX标准Shell(部分系统sh是bash的简化版)

2. 注释语法

bash 复制代码
# 单行注释

: <<'EOF'
多行注释
可以写很多行
EOF

3. 执行方式

bash 复制代码
# 方式1:赋予执行权限后运行(推荐)
chmod +x script.sh
./script.sh

# 方式2:指定解释器运行(无需权限)
bash script.sh
sh script.sh

二、变量与数据类型

1. 变量定义与使用

bash 复制代码
# 定义变量(等号两侧绝对不能有空格!)
name="Alice"
port=8080
path="/var/log"

# 使用变量
echo $name           # 基本用法
echo ${name}_backup  # 推荐:用{}明确变量边界,避免歧义
echo "$name"         # 双引号内会解析变量
echo '$name'         # 单引号内原样输出,不解析

2. 特殊变量

bash 复制代码
$0      # 脚本文件名
$1 $2   # 第1、第2个位置参数
$#      # 参数个数
$*      # 所有参数(作为一个整体字符串)
$@      # 所有参数(每个参数独立,推荐用于循环)
$?      # 上一条命令的返回码(0=成功,非0=失败)
$$      # 当前进程PID
$USER   # 当前登录用户
$PWD    # 当前工作目录

3. 只读变量与删除变量

bash 复制代码
readonly PI=3.14     # 只读,不可修改
unset name           # 删除变量

4. 数值运算

bash 复制代码
a=10
b=3

# 方法1:$(()) 推荐
echo $((a + b))      # 13
echo $((a * b))      # 30
echo $((a % b))      # 1

# 方法2:expr(注意空格)
echo $(expr $a - $b) # 7

# 方法3:bc(支持浮点数)
echo "scale=2; 10 / 3" | bc  # 3.33

5. 字符串操作

bash 复制代码
str="Hello World"
echo ${#str}          # 字符串长度:11
echo ${str:0:5}       # 截取:Hello
echo ${str/World/Bash} # 替换:Hello Bash

三、输入与输出

1. 输出(echo)

bash 复制代码
echo "普通输出"
echo -e "换行\n测试"    # -e 支持转义符
echo "不换行" | tr -d '\n'
echo "错误信息" >&2     # 输出到标准错误流

2. 输入(read)

bash 复制代码
read -p "请输入用户名:" username       # 带提示
read -t 10 -p "请输入密码:" password   # 10秒超时
read -s -p "密码:" pwd                # -s 隐藏输入

3. 重定向

bash 复制代码
echo "内容" > file.log     # 覆盖写入
echo "追加" >> file.log    # 追加写入
./script.sh > out.log 2>&1 # 标准输出+错误输出都写入文件

四、条件判断(if语句)

基本语法

bash 复制代码
if [ 条件 ]; then
    命令
elif [ 条件2 ]; then
    命令
else
    命令
fi

常用判断条件

bash 复制代码
# 文件判断
[ -f "$file" ]    # 文件存在且是普通文件
[ -d "$dir" ]     # 目录存在
[ -e "$path" ]    # 路径存在(不区分类型)
[ -r "$file" ]    # 可读
[ -w "$file" ]    # 可写
[ -x "$file" ]    # 可执行
[ -s "$file" ]    # 文件非空

# 字符串判断
[ -z "$str" ]     # 字符串为空
[ -n "$str" ]     # 字符串非空
[ "$a" = "$b" ]   # 字符串相等
[ "$a" != "$b" ]  # 字符串不等

# 数值比较
[ $a -eq $b ]     # 等于
[ $a -ne $b ]     # 不等于
[ $a -gt $b ]     # 大于
[ $a -lt $b ]     # 小于
[ $a -ge $b ]     # 大于等于
[ $a -le $b ]     # 小于等于

实战示例:检查Nginx服务

bash 复制代码
if ps -ef | grep -v grep | grep -q nginx; then
    echo "Nginx正在运行"
else
    echo "Nginx未运行,正在重启..."
    systemctl restart nginx
fi

五、循环语句

1. for循环

bash 复制代码
# 遍历列表
for i in 1 2 3 4 5; do
    echo "数字:$i"
done

# 范围遍历
for i in {1..10}; do
    echo "第 $i 次"
done

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

# 遍历文件
for file in /var/log/*.log; do
    echo "处理文件:$file"
done

2. while循环

bash 复制代码
count=0
while [ $count -lt 5 ]; do
    echo "当前值:$count"
    count=$((count + 1))
done

# 逐行读取文件
while IFS= read -r line; do
    echo "行内容:$line"
done < file.txt

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

bash 复制代码
count=0
until [ $count -ge 5 ]; do
    echo $count
    count=$((count + 1))
done

六、case分支语句

适用于多值匹配,替代多个if-elif:

bash 复制代码
case $1 in
    start)
        echo "启动服务..."
        systemctl start nginx
        ;;
    stop)
        echo "停止服务..."
        systemctl stop nginx
        ;;
    restart)
        echo "重启服务..."
        systemctl restart nginx
        ;;
    *)
        echo "用法:$0 {start|stop|restart}"
        exit 1
        ;;
esac

七、函数

bash 复制代码
# 定义函数
check_service() {
    local service_name=$1   # local 局部变量
    if systemctl is-active --quiet "$service_name"; then
        echo "$service_name 正在运行"
        return 0
    else
        echo "$service_name 未运行"
        return 1
    fi
}

# 调用函数
check_service nginx
result=$?   # 获取函数返回值
echo "返回码:$result"

八、错误处理与调试

1. 错误中断

bash 复制代码
set -e    # 任何命令失败立即退出脚本
set -u    # 使用未定义变量时报错
set -o pipefail  # 管道中任一命令失败则整体失败

2. 调试模式

bash 复制代码
# 运行时调试
bash -x script.sh

# 脚本内开启/关闭调试
set -x    # 开启:显示每条执行的命令
set +x    # 关闭调试

3. 检查命令执行结果

bash 复制代码
if ! command -v nginx &>/dev/null; then
    echo "nginx未安装"
    exit 1
fi

九、实战案例:日志清理脚本

bash 复制代码
#!/usr/bin/env bash
set -euo pipefail

# 配置
LOG_DIR="/var/log/app"
KEEP_DAYS=30
BACKUP_DIR="/backup/logs"

# 函数:清理过期日志
clean_logs() {
    local dir=$1
    local days=$2
    
    if [ ! -d "$dir" ]; then
        echo "目录不存在:$dir"
        return 1
    fi
    
    echo "清理 $dir 中超过 ${days} 天的日志..."
    find "$dir" -name "*.log" -mtime +$days -delete
    echo "清理完成"
}

# 主逻辑
echo "===== 日志清理开始:$(date) ====="
clean_logs "$LOG_DIR" "$KEEP_DAYS"

# 备份今日日志
mkdir -p "$BACKUP_DIR"
tar -czf "${BACKUP_DIR}/logs_$(date +%Y%m%d).tar.gz" "$LOG_DIR"/*.log 2>/dev/null || true

echo "===== 清理结束:$(date) ====="

十、新手避坑指南

常见错误 正确写法 说明
name = "value" name="value" 等号两侧不能有空格
if [$a -eq $b] if [ $a -eq $b ] [] 内侧必须有空格
echo $var_text echo ${var}_text 用{}明确变量边界
忘记加执行权限 chmod +x script.sh 否则./script.sh报权限错误
不检查返回值 `cmd
硬编码路径 使用变量 便于维护和跨环境部署

总结

Shell脚本的核心语法可以归纳为:变量 → 条件 → 循环 → 函数 → 错误处理。掌握这些基础模块,你就能构建出绝大多数自动化运维脚本。建议从简单的文件操作、服务检查脚本开始练习,逐步过渡到复杂的批量部署和监控告警场景。

💡 学习建议 :多用bash -x调试,多写set -euo pipefail保底,养成检查$?的习惯,你的脚本会越来越健壮!

相关推荐
zandy10112 小时前
体验家 XMPlus 智能客群分群引擎:从 RFM 模型到多维行为画像的动态标签体系设计
大数据·前端·人工智能
DFT计算杂谈2 小时前
WannierTools输入文件wt.in一键批量生成脚本
java·前端·chrome·python·算法·conda
rising start2 小时前
九、vue3 组件通信:全场景详解
前端·vue.js·typescript
VOLUN2 小时前
告别 AI 乱码!Vue3+TS 项目的 AI 编码助手规范实践
前端·ai编程
踏雪羽翼2 小时前
android 实现文字打印机效果
android·前端·javascript
编程技术手记2 小时前
Vue Scoped CSS 与动态创建 DOM 的兼容性问题
前端·css·vue.js
Patrick_Wilson2 小时前
从「框架内部报错」到「请求头被网关截断」:一次 Sentry 排障与前端 Cookie 误用复盘
前端·http·浏览器
Cerrda2 小时前
从 uno.config.ts 看懂 UnoCSS 图标方案
前端·代码规范
爱勇宝2 小时前
《置身钉内》之后:普通前端的出路在哪里?
前端·后端·程序员